8191907: PPC64 and s390 parts of JDK-8174962: Better interface invocations

Reviewed-by: goetz
This commit is contained in:
Martin Doerr 2017-11-28 01:08:26 +03:00
parent 7f2f3c08cb
commit 20439abed6
9 changed files with 195 additions and 256 deletions

View file

@ -1788,11 +1788,10 @@ void MacroAssembler::lookup_interface_method(Register recv_klass,
RegisterOrConstant itable_index, RegisterOrConstant itable_index,
Register method_result, Register method_result,
Register scan_temp, Register scan_temp,
Register sethi_temp, Register temp2,
Label& L_no_such_interface) { Label& L_no_such_interface,
bool return_method) {
assert_different_registers(recv_klass, intf_klass, method_result, scan_temp); assert_different_registers(recv_klass, intf_klass, method_result, scan_temp);
assert(itable_index.is_constant() || itable_index.as_register() == method_result,
"caller must use same register for non-constant itable index as for method");
// Compute start of first itableOffsetEntry (which is at the end of the vtable). // Compute start of first itableOffsetEntry (which is at the end of the vtable).
int vtable_base = in_bytes(Klass::vtable_start_offset()); int vtable_base = in_bytes(Klass::vtable_start_offset());
@ -1810,15 +1809,17 @@ void MacroAssembler::lookup_interface_method(Register recv_klass,
add(scan_temp, recv_klass, scan_temp); add(scan_temp, recv_klass, scan_temp);
// Adjust recv_klass by scaled itable_index, so we can free itable_index. // Adjust recv_klass by scaled itable_index, so we can free itable_index.
if (itable_index.is_register()) { if (return_method) {
Register itable_offset = itable_index.as_register(); if (itable_index.is_register()) {
sldi(itable_offset, itable_offset, logMEsize); Register itable_offset = itable_index.as_register();
if (itentry_off) addi(itable_offset, itable_offset, itentry_off); sldi(method_result, itable_offset, logMEsize);
add(recv_klass, itable_offset, recv_klass); if (itentry_off) { addi(method_result, method_result, itentry_off); }
} else { add(method_result, method_result, recv_klass);
long itable_offset = (long)itable_index.as_constant(); } else {
load_const_optimized(sethi_temp, (itable_offset<<logMEsize)+itentry_off); // static address, no relocation long itable_offset = (long)itable_index.as_constant();
add(recv_klass, sethi_temp, recv_klass); // static address, no relocation
add_const_optimized(method_result, recv_klass, (itable_offset << logMEsize) + itentry_off, temp2);
}
} }
// for (scan = klass->itable(); scan->interface() != NULL; scan += scan_step) { // for (scan = klass->itable(); scan->interface() != NULL; scan += scan_step) {
@ -1831,12 +1832,12 @@ void MacroAssembler::lookup_interface_method(Register recv_klass,
for (int peel = 1; peel >= 0; peel--) { for (int peel = 1; peel >= 0; peel--) {
// %%%% Could load both offset and interface in one ldx, if they were // %%%% Could load both offset and interface in one ldx, if they were
// in the opposite order. This would save a load. // in the opposite order. This would save a load.
ld(method_result, itableOffsetEntry::interface_offset_in_bytes(), scan_temp); ld(temp2, itableOffsetEntry::interface_offset_in_bytes(), scan_temp);
// Check that this entry is non-null. A null entry means that // Check that this entry is non-null. A null entry means that
// the receiver class doesn't implement the interface, and wasn't the // the receiver class doesn't implement the interface, and wasn't the
// same as when the caller was compiled. // same as when the caller was compiled.
cmpd(CCR0, method_result, intf_klass); cmpd(CCR0, temp2, intf_klass);
if (peel) { if (peel) {
beq(CCR0, found_method); beq(CCR0, found_method);
@ -1849,7 +1850,7 @@ void MacroAssembler::lookup_interface_method(Register recv_klass,
bind(search); bind(search);
cmpdi(CCR0, method_result, 0); cmpdi(CCR0, temp2, 0);
beq(CCR0, L_no_such_interface); beq(CCR0, L_no_such_interface);
addi(scan_temp, scan_temp, scan_step); addi(scan_temp, scan_temp, scan_step);
} }
@ -1857,9 +1858,11 @@ void MacroAssembler::lookup_interface_method(Register recv_klass,
bind(found_method); bind(found_method);
// Got a hit. // Got a hit.
int ito_offset = itableOffsetEntry::offset_offset_in_bytes(); if (return_method) {
lwz(scan_temp, ito_offset, scan_temp); int ito_offset = itableOffsetEntry::offset_offset_in_bytes();
ldx(method_result, scan_temp, recv_klass); lwz(scan_temp, ito_offset, scan_temp);
ldx(method_result, scan_temp, method_result);
}
} }
// virtual method calling // virtual method calling

View file

@ -519,7 +519,8 @@ class MacroAssembler: public Assembler {
RegisterOrConstant itable_index, RegisterOrConstant itable_index,
Register method_result, Register method_result,
Register temp_reg, Register temp2_reg, Register temp_reg, Register temp2_reg,
Label& no_such_interface); Label& no_such_interface,
bool return_method = true);
// virtual method calling // virtual method calling
void lookup_virtual_method(Register recv_klass, void lookup_virtual_method(Register recv_klass,

View file

@ -3486,11 +3486,11 @@ void TemplateTable::invokestatic(int byte_no) {
void TemplateTable::invokeinterface_object_method(Register Rrecv_klass, void TemplateTable::invokeinterface_object_method(Register Rrecv_klass,
Register Rret, Register Rret,
Register Rflags, Register Rflags,
Register Rindex, Register Rmethod,
Register Rtemp1, Register Rtemp1,
Register Rtemp2) { Register Rtemp2) {
assert_different_registers(Rindex, Rret, Rrecv_klass, Rflags, Rtemp1, Rtemp2); assert_different_registers(Rmethod, Rret, Rrecv_klass, Rflags, Rtemp1, Rtemp2);
Label LnotFinal; Label LnotFinal;
// Check for vfinal. // Check for vfinal.
@ -3502,14 +3502,14 @@ void TemplateTable::invokeinterface_object_method(Register Rrecv_klass,
// Final call case. // Final call case.
__ profile_final_call(Rtemp1, Rscratch); __ profile_final_call(Rtemp1, Rscratch);
// Argument and return type profiling. // Argument and return type profiling.
__ profile_arguments_type(Rindex, Rscratch, Rrecv_klass /* scratch */, true); __ profile_arguments_type(Rmethod, Rscratch, Rrecv_klass /* scratch */, true);
// Do the final call - the index (f2) contains the method. // Do the final call - the index (f2) contains the method.
__ call_from_interpreter(Rindex, Rret, Rscratch, Rrecv_klass /* scratch */); __ call_from_interpreter(Rmethod, Rret, Rscratch, Rrecv_klass /* scratch */);
// Non-final callc case. // Non-final callc case.
__ bind(LnotFinal); __ bind(LnotFinal);
__ profile_virtual_call(Rrecv_klass, Rtemp1, Rscratch, false); __ profile_virtual_call(Rrecv_klass, Rtemp1, Rscratch, false);
generate_vtable_call(Rrecv_klass, Rindex, Rret, Rscratch); generate_vtable_call(Rrecv_klass, Rmethod, Rret, Rscratch);
} }
void TemplateTable::invokeinterface(int byte_no) { void TemplateTable::invokeinterface(int byte_no) {
@ -3518,58 +3518,61 @@ void TemplateTable::invokeinterface(int byte_no) {
const Register Rscratch1 = R11_scratch1, const Register Rscratch1 = R11_scratch1,
Rscratch2 = R12_scratch2, Rscratch2 = R12_scratch2,
Rscratch3 = R9_ARG7, Rmethod = R6_ARG4,
Rscratch4 = R10_ARG8, Rmethod2 = R9_ARG7,
Rtable_addr = Rscratch2,
Rinterface_klass = R5_ARG3, Rinterface_klass = R5_ARG3,
Rret_type = R8_ARG6, Rret_addr = R8_ARG6,
Rret_addr = Rret_type, Rindex = R10_ARG8,
Rindex = R6_ARG4, Rreceiver = R3_ARG1,
Rreceiver = R4_ARG2, Rrecv_klass = R4_ARG2,
Rrecv_klass = Rreceiver,
Rflags = R7_ARG5; Rflags = R7_ARG5;
prepare_invoke(byte_no, Rinterface_klass, Rret_addr, Rindex, Rreceiver, Rflags, Rscratch1); prepare_invoke(byte_no, Rinterface_klass, Rret_addr, Rmethod, Rreceiver, Rflags, Rscratch1);
// Get receiver klass. // Get receiver klass.
__ null_check_throw(Rreceiver, oopDesc::klass_offset_in_bytes(), Rscratch3); __ null_check_throw(Rreceiver, oopDesc::klass_offset_in_bytes(), Rscratch2);
__ load_klass(Rrecv_klass, Rreceiver); __ load_klass(Rrecv_klass, Rreceiver);
// Check corner case object method. // Check corner case object method.
Label LobjectMethod; Label LobjectMethod, L_no_such_interface, Lthrow_ame;
__ testbitdi(CCR0, R0, Rflags, ConstantPoolCacheEntry::is_forced_virtual_shift); __ testbitdi(CCR0, R0, Rflags, ConstantPoolCacheEntry::is_forced_virtual_shift);
__ btrue(CCR0, LobjectMethod); __ btrue(CCR0, LobjectMethod);
// Fallthrough: The normal invokeinterface case. __ lookup_interface_method(Rrecv_klass, Rinterface_klass, noreg, noreg, Rscratch1, Rscratch2,
L_no_such_interface, /*return_method=*/false);
__ profile_virtual_call(Rrecv_klass, Rscratch1, Rscratch2, false); __ profile_virtual_call(Rrecv_klass, Rscratch1, Rscratch2, false);
// Find entry point to call. // Find entry point to call.
Label Lthrow_icc, Lthrow_ame;
// Result will be returned in Rindex.
__ mr(Rscratch4, Rrecv_klass);
__ mr(Rscratch3, Rindex);
__ lookup_interface_method(Rrecv_klass, Rinterface_klass, Rindex, Rindex, Rscratch1, Rscratch2, Lthrow_icc);
__ cmpdi(CCR0, Rindex, 0); // Get declaring interface class from method
__ ld(Rinterface_klass, in_bytes(Method::const_offset()), Rmethod);
__ ld(Rinterface_klass, in_bytes(ConstMethod::constants_offset()), Rinterface_klass);
__ ld(Rinterface_klass, ConstantPool::pool_holder_offset_in_bytes(), Rinterface_klass);
// Get itable index from method
__ lwa(Rindex, in_bytes(Method::itable_index_offset()), Rmethod);
__ subfic(Rindex, Rindex, Method::itable_index_max);
__ lookup_interface_method(Rrecv_klass, Rinterface_klass, Rindex, Rmethod2, Rscratch1, Rscratch2,
L_no_such_interface);
__ cmpdi(CCR0, Rmethod2, 0);
__ beq(CCR0, Lthrow_ame); __ beq(CCR0, Lthrow_ame);
// Found entry. Jump off! // Found entry. Jump off!
// Argument and return type profiling. // Argument and return type profiling.
__ profile_arguments_type(Rindex, Rscratch1, Rscratch2, true); __ profile_arguments_type(Rmethod2, Rscratch1, Rscratch2, true);
__ call_from_interpreter(Rindex, Rret_addr, Rscratch1, Rscratch2); //__ profile_called_method(Rindex, Rscratch1);
__ call_from_interpreter(Rmethod2, Rret_addr, Rscratch1, Rscratch2);
// Vtable entry was NULL => Throw abstract method error. // Vtable entry was NULL => Throw abstract method error.
__ bind(Lthrow_ame); __ bind(Lthrow_ame);
__ mr(Rrecv_klass, Rscratch4);
__ mr(Rindex, Rscratch3);
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodError)); call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodError));
// Interface was not found => Throw incompatible class change error. // Interface was not found => Throw incompatible class change error.
__ bind(Lthrow_icc); __ bind(L_no_such_interface);
__ mr(Rrecv_klass, Rscratch4);
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_IncompatibleClassChangeError)); call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_IncompatibleClassChangeError));
DEBUG_ONLY( __ should_not_reach_here(); )
__ should_not_reach_here();
// Special case of invokeinterface called for virtual method of // Special case of invokeinterface called for virtual method of
// java.lang.Object. See ConstantPoolCacheEntry::set_method() for details: // java.lang.Object. See ConstantPoolCacheEntry::set_method() for details:
@ -3577,7 +3580,7 @@ void TemplateTable::invokeinterface(int byte_no) {
// to handle this corner case. This code isn't produced by javac, but could // to handle this corner case. This code isn't produced by javac, but could
// be produced by another compliant java compiler. // be produced by another compliant java compiler.
__ bind(LobjectMethod); __ bind(LobjectMethod);
invokeinterface_object_method(Rrecv_klass, Rret_addr, Rflags, Rindex, Rscratch1, Rscratch2); invokeinterface_object_method(Rrecv_klass, Rret_addr, Rflags, Rmethod, Rscratch1, Rscratch2);
} }
void TemplateTable::invokedynamic(int byte_no) { void TemplateTable::invokedynamic(int byte_no) {

View file

@ -1,6 +1,6 @@
/* /*
* Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2016 SAP SE. All rights reserved. * Copyright (c) 2012, 2017 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -28,6 +28,7 @@
#include "code/vtableStubs.hpp" #include "code/vtableStubs.hpp"
#include "interp_masm_ppc.hpp" #include "interp_masm_ppc.hpp"
#include "memory/resourceArea.hpp" #include "memory/resourceArea.hpp"
#include "oops/compiledICHolder.hpp"
#include "oops/instanceKlass.hpp" #include "oops/instanceKlass.hpp"
#include "oops/klassVtable.hpp" #include "oops/klassVtable.hpp"
#include "runtime/sharedRuntime.hpp" #include "runtime/sharedRuntime.hpp"
@ -55,17 +56,22 @@ VtableStub* VtableStubs::create_vtable_stub(int vtable_index) {
// PPC port: use fixed size. // PPC port: use fixed size.
const int code_length = VtableStub::pd_code_size_limit(true); const int code_length = VtableStub::pd_code_size_limit(true);
VtableStub* s = new (code_length) VtableStub(true, vtable_index); VtableStub* s = new (code_length) VtableStub(true, vtable_index);
// Can be NULL if there is no free space in the code cache.
if (s == NULL) {
return NULL;
}
ResourceMark rm; ResourceMark rm;
CodeBuffer cb(s->entry_point(), code_length); CodeBuffer cb(s->entry_point(), code_length);
MacroAssembler* masm = new MacroAssembler(&cb); MacroAssembler* masm = new MacroAssembler(&cb);
address start_pc;
#ifndef PRODUCT #ifndef PRODUCT
if (CountCompiledCalls) { if (CountCompiledCalls) {
__ load_const(R11_scratch1, SharedRuntime::nof_megamorphic_calls_addr()); int offs = __ load_const_optimized(R11_scratch1, SharedRuntime::nof_megamorphic_calls_addr(), R12_scratch2, true);
__ lwz(R12_scratch2, 0, R11_scratch1); __ lwz(R12_scratch2, offs, R11_scratch1);
__ addi(R12_scratch2, R12_scratch2, 1); __ addi(R12_scratch2, R12_scratch2, 1);
__ stw(R12_scratch2, 0, R11_scratch1); __ stw(R12_scratch2, offs, R11_scratch1);
} }
#endif #endif
@ -116,6 +122,7 @@ VtableStub* VtableStubs::create_vtable_stub(int vtable_index) {
__ ld(R12_scratch2, in_bytes(Method::from_compiled_offset()), R19_method); __ ld(R12_scratch2, in_bytes(Method::from_compiled_offset()), R19_method);
__ mtctr(R12_scratch2); __ mtctr(R12_scratch2);
__ bctr(); __ bctr();
masm->flush(); masm->flush();
guarantee(__ pc() <= s->code_end(), "overflowed buffer"); guarantee(__ pc() <= s->code_end(), "overflowed buffer");
@ -125,10 +132,16 @@ VtableStub* VtableStubs::create_vtable_stub(int vtable_index) {
return s; return s;
} }
VtableStub* VtableStubs::create_itable_stub(int vtable_index) { VtableStub* VtableStubs::create_itable_stub(int itable_index) {
// PPC port: use fixed size. // PPC port: use fixed size.
const int code_length = VtableStub::pd_code_size_limit(false); const int code_length = VtableStub::pd_code_size_limit(false);
VtableStub* s = new (code_length) VtableStub(false, vtable_index); VtableStub* s = new (code_length) VtableStub(false, itable_index);
// Can be NULL if there is no free space in the code cache.
if (s == NULL) {
return NULL;
}
ResourceMark rm; ResourceMark rm;
CodeBuffer cb(s->entry_point(), code_length); CodeBuffer cb(s->entry_point(), code_length);
MacroAssembler* masm = new MacroAssembler(&cb); MacroAssembler* masm = new MacroAssembler(&cb);
@ -136,10 +149,10 @@ VtableStub* VtableStubs::create_itable_stub(int vtable_index) {
#ifndef PRODUCT #ifndef PRODUCT
if (CountCompiledCalls) { if (CountCompiledCalls) {
__ load_const(R11_scratch1, SharedRuntime::nof_megamorphic_calls_addr()); int offs = __ load_const_optimized(R11_scratch1, SharedRuntime::nof_megamorphic_calls_addr(), R12_scratch2, true);
__ lwz(R12_scratch2, 0, R11_scratch1); __ lwz(R12_scratch2, offs, R11_scratch1);
__ addi(R12_scratch2, R12_scratch2, 1); __ addi(R12_scratch2, R12_scratch2, 1);
__ stw(R12_scratch2, 0, R11_scratch1); __ stw(R12_scratch2, offs, R11_scratch1);
} }
#endif #endif
@ -148,62 +161,28 @@ VtableStub* VtableStubs::create_itable_stub(int vtable_index) {
// Entry arguments: // Entry arguments:
// R19_method: Interface // R19_method: Interface
// R3_ARG1: Receiver // R3_ARG1: Receiver
//
const Register rcvr_klass = R11_scratch1; Label L_no_such_interface;
const Register vtable_len = R12_scratch2; const Register rcvr_klass = R11_scratch1,
const Register itable_entry_addr = R21_tmp1; interface = R12_scratch2,
const Register itable_interface = R22_tmp2; tmp1 = R21_tmp1,
tmp2 = R22_tmp2;
// Get receiver klass.
// We might implicit NULL fault here.
address npe_addr = __ pc(); // npe = null pointer exception address npe_addr = __ pc(); // npe = null pointer exception
__ null_check(R3_ARG1, oopDesc::klass_offset_in_bytes(), /*implicit only*/NULL); __ null_check(R3_ARG1, oopDesc::klass_offset_in_bytes(), /*implicit only*/NULL);
__ load_klass(rcvr_klass, R3_ARG1); __ load_klass(rcvr_klass, R3_ARG1);
BLOCK_COMMENT("Load start of itable entries into itable_entry."); // Receiver subtype check against REFC.
__ lwz(vtable_len, in_bytes(Klass::vtable_length_offset()), rcvr_klass); __ ld(interface, CompiledICHolder::holder_klass_offset(), R19_method);
__ slwi(vtable_len, vtable_len, exact_log2(vtableEntry::size_in_bytes())); __ lookup_interface_method(rcvr_klass, interface, noreg,
__ add(itable_entry_addr, vtable_len, rcvr_klass); R0, tmp1, tmp2,
L_no_such_interface, /*return_method=*/ false);
// Loop over all itable entries until desired interfaceOop(Rinterface) found. // Get Method* and entrypoint for compiler
BLOCK_COMMENT("Increment itable_entry_addr in loop."); __ ld(interface, CompiledICHolder::holder_metadata_offset(), R19_method);
const int vtable_base_offset = in_bytes(Klass::vtable_start_offset()); __ lookup_interface_method(rcvr_klass, interface, itable_index,
__ addi(itable_entry_addr, itable_entry_addr, vtable_base_offset + itableOffsetEntry::interface_offset_in_bytes()); R19_method, tmp1, tmp2,
L_no_such_interface, /*return_method=*/ true);
const int itable_offset_search_inc = itableOffsetEntry::size() * wordSize;
Label search;
__ bind(search);
__ ld(itable_interface, 0, itable_entry_addr);
// Handle IncompatibleClassChangeError in itable stubs.
// If the entry is NULL then we've reached the end of the table
// without finding the expected interface, so throw an exception.
BLOCK_COMMENT("Handle IncompatibleClassChangeError in itable stubs.");
Label throw_icce;
__ cmpdi(CCR1, itable_interface, 0);
__ cmpd(CCR0, itable_interface, R19_method);
__ addi(itable_entry_addr, itable_entry_addr, itable_offset_search_inc);
__ beq(CCR1, throw_icce);
__ bne(CCR0, search);
// Entry found and itable_entry_addr points to it, get offset of vtable for interface.
const Register vtable_offset = R12_scratch2;
const Register itable_method = R11_scratch1;
const int vtable_offset_offset = (itableOffsetEntry::offset_offset_in_bytes() -
itableOffsetEntry::interface_offset_in_bytes()) -
itable_offset_search_inc;
__ lwz(vtable_offset, vtable_offset_offset, itable_entry_addr);
// Compute itableMethodEntry and get method and entry point for compiler.
const int method_offset = (itableMethodEntry::size() * wordSize * vtable_index) +
itableMethodEntry::method_offset_in_bytes();
__ add(itable_method, rcvr_klass, vtable_offset);
__ ld(R19_method, method_offset, itable_method);
#ifndef PRODUCT #ifndef PRODUCT
if (DebugVtables) { if (DebugVtables) {
@ -219,7 +198,7 @@ VtableStub* VtableStubs::create_itable_stub(int vtable_index) {
address ame_addr = __ pc(); // ame = abstract method error address ame_addr = __ pc(); // ame = abstract method error
// Must do an explicit check if implicit checks are disabled. // Must do an explicit check if implicit checks are disabled.
__ null_check(R19_method, in_bytes(Method::from_compiled_offset()), &throw_icce); __ null_check(R19_method, in_bytes(Method::from_compiled_offset()), &L_no_such_interface);
__ ld(R12_scratch2, in_bytes(Method::from_compiled_offset()), R19_method); __ ld(R12_scratch2, in_bytes(Method::from_compiled_offset()), R19_method);
__ mtctr(R12_scratch2); __ mtctr(R12_scratch2);
__ bctr(); __ bctr();
@ -229,8 +208,8 @@ VtableStub* VtableStubs::create_itable_stub(int vtable_index) {
// We force resolving of the call site by jumping to the "handle // We force resolving of the call site by jumping to the "handle
// wrong method" stub, and so let the interpreter runtime do all the // wrong method" stub, and so let the interpreter runtime do all the
// dirty work. // dirty work.
__ bind(throw_icce); __ bind(L_no_such_interface);
__ load_const(R11_scratch1, SharedRuntime::get_handle_wrong_method_stub()); __ load_const_optimized(R11_scratch1, SharedRuntime::get_handle_wrong_method_stub(), R12_scratch2);
__ mtctr(R11_scratch1); __ mtctr(R11_scratch1);
__ bctr(); __ bctr();
@ -245,14 +224,15 @@ VtableStub* VtableStubs::create_itable_stub(int vtable_index) {
int VtableStub::pd_code_size_limit(bool is_vtable_stub) { int VtableStub::pd_code_size_limit(bool is_vtable_stub) {
if (DebugVtables || CountCompiledCalls || VerifyOops) { if (DebugVtables || CountCompiledCalls || VerifyOops) {
return 1000; return 1000;
} else {
int decode_klass_size = MacroAssembler::instr_size_for_decode_klass_not_null();
if (is_vtable_stub) {
return 20 + decode_klass_size + 8 + 8; // Plain + cOops + Traps + safety
} else {
return 96 + decode_klass_size + 12 + 8; // Plain + cOops + Traps + safety
}
} }
int size = is_vtable_stub ? 20 + 8 : 164 + 20; // Plain + safety
if (UseCompressedClassPointers) {
size += MacroAssembler::instr_size_for_decode_klass_not_null();
}
if (!ImplicitNullChecks || !os::zero_page_read_protected()) {
size += is_vtable_stub ? 8 : 12;
}
return size;
} }
int VtableStub::pd_code_alignment() { int VtableStub::pd_code_alignment() {

View file

@ -2806,8 +2806,8 @@ void MacroAssembler::lookup_interface_method(Register recv_klass,
RegisterOrConstant itable_index, RegisterOrConstant itable_index,
Register method_result, Register method_result,
Register temp1_reg, Register temp1_reg,
Register temp2_reg, Label& no_such_interface,
Label& no_such_interface) { bool return_method) {
const Register vtable_len = temp1_reg; // Used to compute itable_entry_addr. const Register vtable_len = temp1_reg; // Used to compute itable_entry_addr.
const Register itable_entry_addr = Z_R1_scratch; const Register itable_entry_addr = Z_R1_scratch;
@ -2842,38 +2842,36 @@ void MacroAssembler::lookup_interface_method(Register recv_klass,
z_brne(search); z_brne(search);
// Entry found and itable_entry_addr points to it, get offset of vtable for interface. // Entry found and itable_entry_addr points to it, get offset of vtable for interface.
if (return_method) {
const int vtable_offset_offset = (itableOffsetEntry::offset_offset_in_bytes() -
itableOffsetEntry::interface_offset_in_bytes()) -
itable_offset_search_inc;
const int vtable_offset_offset = (itableOffsetEntry::offset_offset_in_bytes() - // Compute itableMethodEntry and get method and entry point
itableOffsetEntry::interface_offset_in_bytes()) - // we use addressing with index and displacement, since the formula
itable_offset_search_inc; // for computing the entry's offset has a fixed and a dynamic part,
// the latter depending on the matched interface entry and on the case,
// that the itable index has been passed as a register, not a constant value.
int method_offset = itableMethodEntry::method_offset_in_bytes();
// Fixed part (displacement), common operand.
Register itable_offset = method_result; // Dynamic part (index register).
// Compute itableMethodEntry and get method and entry point if (itable_index.is_register()) {
// we use addressing with index and displacement, since the formula // Compute the method's offset in that register, for the formula, see the
// for computing the entry's offset has a fixed and a dynamic part, // else-clause below.
// the latter depending on the matched interface entry and on the case, z_sllg(itable_offset, itable_index.as_register(), exact_log2(itableMethodEntry::size() * wordSize));
// that the itable index has been passed as a register, not a constant value. z_agf(itable_offset, vtable_offset_offset, itable_entry_addr);
int method_offset = itableMethodEntry::method_offset_in_bytes(); } else {
// Fixed part (displacement), common operand. // Displacement increases.
Register itable_offset; // Dynamic part (index register). method_offset += itableMethodEntry::size() * wordSize * itable_index.as_constant();
if (itable_index.is_register()) { // Load index from itable.
// Compute the method's offset in that register, for the formula, see the z_llgf(itable_offset, vtable_offset_offset, itable_entry_addr);
// else-clause below. }
itable_offset = itable_index.as_register();
z_sllg(itable_offset, itable_offset, exact_log2(itableMethodEntry::size() * wordSize)); // Finally load the method's oop.
z_agf(itable_offset, vtable_offset_offset, itable_entry_addr); z_lg(method_result, method_offset, itable_offset, recv_klass);
} else {
itable_offset = Z_R1_scratch;
// Displacement increases.
method_offset += itableMethodEntry::size() * wordSize * itable_index.as_constant();
// Load index from itable.
z_llgf(itable_offset, vtable_offset_offset, itable_entry_addr);
} }
// Finally load the method's oop.
z_lg(method_result, method_offset, itable_offset, recv_klass);
BLOCK_COMMENT("} lookup_interface_method"); BLOCK_COMMENT("} lookup_interface_method");
} }

View file

@ -671,8 +671,8 @@ class MacroAssembler: public Assembler {
RegisterOrConstant itable_index, RegisterOrConstant itable_index,
Register method_result, Register method_result,
Register temp1_reg, Register temp1_reg,
Register temp2_reg, Label& no_such_interface,
Label& no_such_interface); bool return_method = true);
// virtual method calling // virtual method calling
void lookup_virtual_method(Register recv_klass, void lookup_virtual_method(Register recv_klass,

View file

@ -498,7 +498,7 @@ void MethodHandles::generate_method_handle_dispatch(MacroAssembler* _masm,
Label L_no_such_interface; Label L_no_such_interface;
__ lookup_interface_method(temp1_recv_klass, temp3_intf, __ lookup_interface_method(temp1_recv_klass, temp3_intf,
// Note: next two args must be the same: // Note: next two args must be the same:
Z_index, Z_method, temp2, noreg, Z_index, Z_method, temp2,
L_no_such_interface); L_no_such_interface);
jump_from_method_handle(_masm, Z_method, temp2, Z_R0, for_compiler_entry); jump_from_method_handle(_masm, Z_method, temp2, Z_R0, for_compiler_entry);

View file

@ -3557,66 +3557,67 @@ void TemplateTable::invokeinterface(int byte_no) {
transition(vtos, vtos); transition(vtos, vtos);
assert(byte_no == f1_byte, "use this argument"); assert(byte_no == f1_byte, "use this argument");
Register interface = Z_tos; Register klass = Z_ARG2,
Register index = Z_ARG3; method = Z_ARG3,
Register receiver = Z_tmp_1; interface = Z_ARG4,
Register flags = Z_ARG5; flags = Z_ARG5,
receiver = Z_tmp_1;
BLOCK_COMMENT("invokeinterface {"); BLOCK_COMMENT("invokeinterface {");
// Destroys Z_ARG1 and Z_ARG2, thus use Z_ARG4 and copy afterwards. prepare_invoke(byte_no, interface, method, // Get f1 klassOop, f2 itable index.
prepare_invoke(byte_no, Z_ARG4, index, // Get f1 klassOop, f2 itable index.
receiver, flags); receiver, flags);
// Z_R14 (== Z_bytecode) : return entry // Z_R14 (== Z_bytecode) : return entry
__ z_lgr(interface, Z_ARG4);
// Special case of invokeinterface called for virtual method of // Special case of invokeinterface called for virtual method of
// java.lang.Object. See cpCacheOop.cpp for details. // java.lang.Object. See cpCacheOop.cpp for details.
// This code isn't produced by javac, but could be produced by // This code isn't produced by javac, but could be produced by
// another compliant java compiler. // another compliant java compiler.
Label notMethod; NearLabel notMethod, no_such_interface, no_such_method;
__ testbit(flags, ConstantPoolCacheEntry::is_forced_virtual_shift); __ testbit(flags, ConstantPoolCacheEntry::is_forced_virtual_shift);
__ z_brz(notMethod); __ z_brz(notMethod);
invokevirtual_helper(index, receiver, flags); invokevirtual_helper(method, receiver, flags);
__ bind(notMethod); __ bind(notMethod);
// Get receiver klass into klass - also a null check. // Get receiver klass into klass - also a null check.
Register klass = flags;
__ restore_locals(); __ restore_locals();
__ load_klass(klass, receiver); __ load_klass(klass, receiver);
__ lookup_interface_method(klass, interface, noreg, noreg, /*temp*/Z_ARG1,
no_such_interface, /*return_method=*/false);
// Profile this call. // Profile this call.
__ profile_virtual_call(klass, Z_ARG2/*mdp*/, Z_ARG4/*scratch*/); __ profile_virtual_call(klass, Z_ARG1/*mdp*/, flags/*scratch*/);
NearLabel no_such_interface, no_such_method; // Find entry point to call.
Register method = Z_tmp_2;
// TK 2010-08-24: save the index to Z_ARG4. needed in case of an error // Get declaring interface class from method
// in throw_AbstractMethodErrorByTemplateTable __ z_lg(interface, Address(method, Method::const_offset()));
__ z_lgr(Z_ARG4, index); __ z_lg(interface, Address(interface, ConstMethod::constants_offset()));
// TK 2011-03-24: copy also klass because it could be changed in __ z_lg(interface, Address(interface, ConstantPool::pool_holder_offset_in_bytes()));
// lookup_interface_method
__ z_lgr(Z_ARG2, klass); // Get itable index from method
__ lookup_interface_method(// inputs: rec. class, interface, itable index Register index = receiver,
klass, interface, index, method2 = flags;
// outputs: method, scan temp. reg __ z_lgf(index, Address(method, Method::itable_index_offset()));
method, Z_tmp_2, Z_R1_scratch, __ z_aghi(index, -Method::itable_index_max);
no_such_interface); __ z_lcgr(index, index);
__ lookup_interface_method(klass, interface, index, method2, Z_tmp_2,
no_such_interface);
// Check for abstract method error. // Check for abstract method error.
// Note: This should be done more efficiently via a throw_abstract_method_error // Note: This should be done more efficiently via a throw_abstract_method_error
// interpreter entry point and a conditional jump to it in case of a null // interpreter entry point and a conditional jump to it in case of a null
// method. // method.
__ compareU64_and_branch(method, (intptr_t) 0, __ compareU64_and_branch(method2, (intptr_t) 0,
Assembler::bcondZero, no_such_method); Assembler::bcondZero, no_such_method);
__ profile_arguments_type(Z_ARG3, method, Z_ARG5, true); __ profile_arguments_type(Z_tmp_1, method2, Z_tmp_2, true);
// Do the call. // Do the call.
__ jump_from_interpreted(method, Z_ARG5); __ jump_from_interpreted(method2, Z_tmp_2);
__ should_not_reach_here(); __ should_not_reach_here();
// exception handling code follows... // exception handling code follows...
@ -3628,12 +3629,8 @@ void TemplateTable::invokeinterface(int byte_no) {
// Throw exception. // Throw exception.
__ restore_bcp(); // Bcp must be correct for exception handler (was destroyed). __ restore_bcp(); // Bcp must be correct for exception handler (was destroyed).
__ restore_locals(); // Make sure locals pointer is correct as well (was destroyed). __ restore_locals(); // Make sure locals pointer is correct as well (was destroyed).
// TK 2010-08-24: Call throw_AbstractMethodErrorByTemplateTable now with the
// relevant information for generating a better error message
__ call_VM(noreg, __ call_VM(noreg,
CAST_FROM_FN_PTR(address, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodError));
InterpreterRuntime::throw_AbstractMethodError),
Z_ARG2, interface, Z_ARG4);
// The call_VM checks for exception, so we should never return here. // The call_VM checks for exception, so we should never return here.
__ should_not_reach_here(); __ should_not_reach_here();
@ -3642,12 +3639,8 @@ void TemplateTable::invokeinterface(int byte_no) {
// Throw exception. // Throw exception.
__ restore_bcp(); // Bcp must be correct for exception handler (was destroyed). __ restore_bcp(); // Bcp must be correct for exception handler (was destroyed).
__ restore_locals(); // Make sure locals pointer is correct as well (was destroyed). __ restore_locals(); // Make sure locals pointer is correct as well (was destroyed).
// TK 2010-08-24: Call throw_IncompatibleClassChangeErrorByTemplateTable now with the
// relevant information for generating a better error message
__ call_VM(noreg, __ call_VM(noreg,
CAST_FROM_FN_PTR(address, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_IncompatibleClassChangeError));
InterpreterRuntime::throw_IncompatibleClassChangeError),
Z_ARG2, interface);
// The call_VM checks for exception, so we should never return here. // The call_VM checks for exception, so we should never return here.
__ should_not_reach_here(); __ should_not_reach_here();

View file

@ -1,6 +1,6 @@
/* /*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016 SAP SE. All rights reserved. * Copyright (c) 2016, 2017 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -28,6 +28,7 @@
#include "code/vtableStubs.hpp" #include "code/vtableStubs.hpp"
#include "interp_masm_s390.hpp" #include "interp_masm_s390.hpp"
#include "memory/resourceArea.hpp" #include "memory/resourceArea.hpp"
#include "oops/compiledICHolder.hpp"
#include "oops/instanceKlass.hpp" #include "oops/instanceKlass.hpp"
#include "oops/klassVtable.hpp" #include "oops/klassVtable.hpp"
#include "runtime/sharedRuntime.hpp" #include "runtime/sharedRuntime.hpp"
@ -57,7 +58,6 @@ VtableStub* VtableStubs::create_vtable_stub(int vtable_index) {
ResourceMark rm; ResourceMark rm;
CodeBuffer cb(s->entry_point(), code_length); CodeBuffer cb(s->entry_point(), code_length);
MacroAssembler *masm = new MacroAssembler(&cb); MacroAssembler *masm = new MacroAssembler(&cb);
address start_pc;
int padding_bytes = 0; int padding_bytes = 0;
#if (!defined(PRODUCT) && defined(COMPILER2)) #if (!defined(PRODUCT) && defined(COMPILER2))
@ -144,9 +144,9 @@ VtableStub* VtableStubs::create_vtable_stub(int vtable_index) {
return s; return s;
} }
VtableStub* VtableStubs::create_itable_stub(int vtable_index) { VtableStub* VtableStubs::create_itable_stub(int itable_index) {
const int code_length = VtableStub::pd_code_size_limit(false); const int code_length = VtableStub::pd_code_size_limit(false);
VtableStub *s = new(code_length) VtableStub(false, vtable_index); VtableStub *s = new(code_length) VtableStub(false, itable_index);
if (s == NULL) { // Indicates OOM in the code cache. if (s == NULL) { // Indicates OOM in the code cache.
return NULL; return NULL;
} }
@ -154,7 +154,6 @@ VtableStub* VtableStubs::create_itable_stub(int vtable_index) {
ResourceMark rm; ResourceMark rm;
CodeBuffer cb(s->entry_point(), code_length); CodeBuffer cb(s->entry_point(), code_length);
MacroAssembler *masm = new MacroAssembler(&cb); MacroAssembler *masm = new MacroAssembler(&cb);
address start_pc;
int padding_bytes = 0; int padding_bytes = 0;
#if (!defined(PRODUCT) && defined(COMPILER2)) #if (!defined(PRODUCT) && defined(COMPILER2))
@ -174,11 +173,9 @@ VtableStub* VtableStubs::create_itable_stub(int vtable_index) {
// Entry arguments: // Entry arguments:
// Z_method: Interface // Z_method: Interface
// Z_ARG1: Receiver // Z_ARG1: Receiver
const Register rcvr_klass = Z_tmp_1; // Used to compute itable_entry_addr. NearLabel no_such_interface;
// Use extra reg to avoid re-load. const Register rcvr_klass = Z_tmp_1,
const Register vtable_len = Z_tmp_2; // Used to compute itable_entry_addr. interface = Z_tmp_2;
const Register itable_entry_addr = Z_R1_scratch;
const Register itable_interface = Z_R0_scratch;
// Get receiver klass. // Get receiver klass.
// Must do an explicit check if implicit checks are disabled. // Must do an explicit check if implicit checks are disabled.
@ -186,50 +183,15 @@ VtableStub* VtableStubs::create_itable_stub(int vtable_index) {
__ null_check(Z_ARG1, Z_R1_scratch, oopDesc::klass_offset_in_bytes()); __ null_check(Z_ARG1, Z_R1_scratch, oopDesc::klass_offset_in_bytes());
__ load_klass(rcvr_klass, Z_ARG1); __ load_klass(rcvr_klass, Z_ARG1);
// Load start of itable entries into itable_entry. // Receiver subtype check against REFC.
__ z_llgf(vtable_len, Address(rcvr_klass, Klass::vtable_length_offset())); __ z_lg(interface, Address(Z_method, CompiledICHolder::holder_klass_offset()));
__ z_sllg(vtable_len, vtable_len, exact_log2(vtableEntry::size_in_bytes())); __ lookup_interface_method(rcvr_klass, interface, noreg,
noreg, Z_R1, no_such_interface, /*return_method=*/ false);
// Loop over all itable entries until desired interfaceOop(Rinterface) found. // Get Method* and entrypoint for compiler
const int vtable_base_offset = in_bytes(Klass::vtable_start_offset()); __ z_lg(interface, Address(Z_method, CompiledICHolder::holder_metadata_offset()));
// Count unused bytes. __ lookup_interface_method(rcvr_klass, interface, itable_index,
start_pc = __ pc(); Z_method, Z_R1, no_such_interface, /*return_method=*/ true);
__ add2reg_with_index(itable_entry_addr, vtable_base_offset + itableOffsetEntry::interface_offset_in_bytes(), rcvr_klass, vtable_len);
padding_bytes += 20 - (__ pc() - start_pc);
const int itable_offset_search_inc = itableOffsetEntry::size() * wordSize;
Label search;
__ bind(search);
// Handle IncompatibleClassChangeError in itable stubs.
// If the entry is NULL then we've reached the end of the table
// without finding the expected interface, so throw an exception.
NearLabel throw_icce;
__ load_and_test_long(itable_interface, Address(itable_entry_addr));
__ z_bre(throw_icce); // Throw the exception out-of-line.
// Count unused bytes.
start_pc = __ pc();
__ add2reg(itable_entry_addr, itable_offset_search_inc);
padding_bytes += 20 - (__ pc() - start_pc);
__ z_cgr(itable_interface, Z_method);
__ z_brne(search);
// Entry found. Itable_entry_addr points to the subsequent entry (itable_offset_search_inc too far).
// Get offset of vtable for interface.
const Register vtable_offset = Z_R1_scratch;
const Register itable_method = rcvr_klass; // Calculated before.
const int vtable_offset_offset = (itableOffsetEntry::offset_offset_in_bytes() -
itableOffsetEntry::interface_offset_in_bytes()) -
itable_offset_search_inc;
__ z_llgf(vtable_offset, vtable_offset_offset, itable_entry_addr);
// Compute itableMethodEntry and get method and entry point for compiler.
const int method_offset = (itableMethodEntry::size() * wordSize * vtable_index) +
itableMethodEntry::method_offset_in_bytes();
__ z_lg(Z_method, method_offset, vtable_offset, itable_method);
#ifndef PRODUCT #ifndef PRODUCT
if (DebugVtables) { if (DebugVtables) {
@ -244,13 +206,13 @@ VtableStub* VtableStubs::create_itable_stub(int vtable_index) {
address ame_addr = __ pc(); address ame_addr = __ pc();
// Must do an explicit check if implicit checks are disabled. // Must do an explicit check if implicit checks are disabled.
if (!ImplicitNullChecks) { if (!ImplicitNullChecks) {
__ compare64_and_branch(Z_method, (intptr_t) 0, Assembler::bcondEqual, throw_icce); __ compare64_and_branch(Z_method, (intptr_t) 0, Assembler::bcondEqual, no_such_interface);
} }
__ z_lg(Z_R1_scratch, in_bytes(Method::from_compiled_offset()), Z_method); __ z_lg(Z_R1_scratch, in_bytes(Method::from_compiled_offset()), Z_method);
__ z_br(Z_R1_scratch); __ z_br(Z_R1_scratch);
// Handle IncompatibleClassChangeError in itable stubs. // Handle IncompatibleClassChangeError in itable stubs.
__ bind(throw_icce); __ bind(no_such_interface);
// Count unused bytes // Count unused bytes
// worst case actual size // worst case actual size
// We force resolving of the call site by jumping to // We force resolving of the call site by jumping to
@ -273,13 +235,12 @@ int VtableStub::pd_code_size_limit(bool is_vtable_stub) {
if (CountCompiledCalls) { if (CountCompiledCalls) {
size += 6 * 4; size += 6 * 4;
} }
if (is_vtable_stub) { size += is_vtable_stub ? 36 : 140;
size += 52; if (UseCompressedClassPointers) {
} else { size += MacroAssembler::instr_size_for_decode_klass_not_null();
size += 104;
} }
if (Universe::narrow_klass_base() != NULL) { if (!ImplicitNullChecks) {
size += 16; // A guess. size += 36;
} }
return size; return size;
} }