mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-26 22:34:27 +02:00
6813212: factor duplicated assembly code for general subclass check (for 6655638)
Code in interp_masm, stubGenerator, c1_LIRAssembler, and AD files moved into MacroAssembler. Reviewed-by: kvn
This commit is contained in:
parent
de67e52949
commit
b8dbe8d8f6
17 changed files with 734 additions and 617 deletions
|
@ -2767,6 +2767,268 @@ void MacroAssembler::lookup_interface_method(Register recv_klass,
|
|||
}
|
||||
|
||||
|
||||
void MacroAssembler::check_klass_subtype(Register sub_klass,
|
||||
Register super_klass,
|
||||
Register temp_reg,
|
||||
Register temp2_reg,
|
||||
Label& L_success) {
|
||||
Label L_failure, L_pop_to_failure;
|
||||
check_klass_subtype_fast_path(sub_klass, super_klass,
|
||||
temp_reg, temp2_reg,
|
||||
&L_success, &L_failure, NULL);
|
||||
Register sub_2 = sub_klass;
|
||||
Register sup_2 = super_klass;
|
||||
if (!sub_2->is_global()) sub_2 = L0;
|
||||
if (!sup_2->is_global()) sup_2 = L1;
|
||||
|
||||
save_frame_and_mov(0, sub_klass, sub_2, super_klass, sup_2);
|
||||
check_klass_subtype_slow_path(sub_2, sup_2,
|
||||
L2, L3, L4, L5,
|
||||
NULL, &L_pop_to_failure);
|
||||
|
||||
// on success:
|
||||
restore();
|
||||
ba(false, L_success);
|
||||
delayed()->nop();
|
||||
|
||||
// on failure:
|
||||
bind(L_pop_to_failure);
|
||||
restore();
|
||||
bind(L_failure);
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::check_klass_subtype_fast_path(Register sub_klass,
|
||||
Register super_klass,
|
||||
Register temp_reg,
|
||||
Register temp2_reg,
|
||||
Label* L_success,
|
||||
Label* L_failure,
|
||||
Label* L_slow_path,
|
||||
RegisterConstant super_check_offset,
|
||||
Register instanceof_hack) {
|
||||
int sc_offset = (klassOopDesc::header_size() * HeapWordSize +
|
||||
Klass::secondary_super_cache_offset_in_bytes());
|
||||
int sco_offset = (klassOopDesc::header_size() * HeapWordSize +
|
||||
Klass::super_check_offset_offset_in_bytes());
|
||||
|
||||
bool must_load_sco = (super_check_offset.constant_or_zero() == -1);
|
||||
bool need_slow_path = (must_load_sco ||
|
||||
super_check_offset.constant_or_zero() == sco_offset);
|
||||
|
||||
assert_different_registers(sub_klass, super_klass, temp_reg);
|
||||
if (super_check_offset.is_register()) {
|
||||
assert_different_registers(sub_klass, super_klass,
|
||||
super_check_offset.as_register());
|
||||
} else if (must_load_sco) {
|
||||
assert(temp2_reg != noreg, "supply either a temp or a register offset");
|
||||
}
|
||||
|
||||
Label L_fallthrough;
|
||||
int label_nulls = 0;
|
||||
if (L_success == NULL) { L_success = &L_fallthrough; label_nulls++; }
|
||||
if (L_failure == NULL) { L_failure = &L_fallthrough; label_nulls++; }
|
||||
if (L_slow_path == NULL) { L_slow_path = &L_fallthrough; label_nulls++; }
|
||||
assert(label_nulls <= 1 || instanceof_hack != noreg ||
|
||||
(L_slow_path == &L_fallthrough && label_nulls <= 2 && !need_slow_path),
|
||||
"at most one NULL in the batch, usually");
|
||||
|
||||
// Support for the instanceof hack, which uses delay slots to
|
||||
// set a destination register to zero or one.
|
||||
bool do_bool_sets = (instanceof_hack != noreg);
|
||||
#define BOOL_SET(bool_value) \
|
||||
if (do_bool_sets && bool_value >= 0) \
|
||||
set(bool_value, instanceof_hack)
|
||||
#define DELAYED_BOOL_SET(bool_value) \
|
||||
if (do_bool_sets && bool_value >= 0) \
|
||||
delayed()->set(bool_value, instanceof_hack); \
|
||||
else delayed()->nop()
|
||||
// Hacked ba(), which may only be used just before L_fallthrough.
|
||||
#define FINAL_JUMP(label, bool_value) \
|
||||
if (&(label) == &L_fallthrough) { \
|
||||
BOOL_SET(bool_value); \
|
||||
} else { \
|
||||
ba((do_bool_sets && bool_value >= 0), label); \
|
||||
DELAYED_BOOL_SET(bool_value); \
|
||||
}
|
||||
|
||||
// If the pointers are equal, we are done (e.g., String[] elements).
|
||||
// This self-check enables sharing of secondary supertype arrays among
|
||||
// non-primary types such as array-of-interface. Otherwise, each such
|
||||
// type would need its own customized SSA.
|
||||
// We move this check to the front of the fast path because many
|
||||
// type checks are in fact trivially successful in this manner,
|
||||
// so we get a nicely predicted branch right at the start of the check.
|
||||
cmp(super_klass, sub_klass);
|
||||
brx(Assembler::equal, do_bool_sets, Assembler::pn, *L_success);
|
||||
DELAYED_BOOL_SET(1);
|
||||
|
||||
// Check the supertype display:
|
||||
if (must_load_sco) {
|
||||
// The super check offset is always positive...
|
||||
lduw(super_klass, sco_offset, temp2_reg);
|
||||
super_check_offset = RegisterConstant(temp2_reg);
|
||||
}
|
||||
ld_ptr(sub_klass, super_check_offset, temp_reg);
|
||||
cmp(super_klass, temp_reg);
|
||||
|
||||
// This check has worked decisively for primary supers.
|
||||
// Secondary supers are sought in the super_cache ('super_cache_addr').
|
||||
// (Secondary supers are interfaces and very deeply nested subtypes.)
|
||||
// This works in the same check above because of a tricky aliasing
|
||||
// between the super_cache and the primary super display elements.
|
||||
// (The 'super_check_addr' can address either, as the case requires.)
|
||||
// Note that the cache is updated below if it does not help us find
|
||||
// what we need immediately.
|
||||
// So if it was a primary super, we can just fail immediately.
|
||||
// Otherwise, it's the slow path for us (no success at this point).
|
||||
|
||||
if (super_check_offset.is_register()) {
|
||||
brx(Assembler::equal, do_bool_sets, Assembler::pn, *L_success);
|
||||
delayed(); if (do_bool_sets) BOOL_SET(1);
|
||||
// if !do_bool_sets, sneak the next cmp into the delay slot:
|
||||
cmp(super_check_offset.as_register(), sc_offset);
|
||||
|
||||
if (L_failure == &L_fallthrough) {
|
||||
brx(Assembler::equal, do_bool_sets, Assembler::pt, *L_slow_path);
|
||||
delayed()->nop();
|
||||
BOOL_SET(0); // fallthrough on failure
|
||||
} else {
|
||||
brx(Assembler::notEqual, do_bool_sets, Assembler::pn, *L_failure);
|
||||
DELAYED_BOOL_SET(0);
|
||||
FINAL_JUMP(*L_slow_path, -1); // -1 => vanilla delay slot
|
||||
}
|
||||
} else if (super_check_offset.as_constant() == sc_offset) {
|
||||
// Need a slow path; fast failure is impossible.
|
||||
if (L_slow_path == &L_fallthrough) {
|
||||
brx(Assembler::equal, do_bool_sets, Assembler::pt, *L_success);
|
||||
DELAYED_BOOL_SET(1);
|
||||
} else {
|
||||
brx(Assembler::notEqual, false, Assembler::pn, *L_slow_path);
|
||||
delayed()->nop();
|
||||
FINAL_JUMP(*L_success, 1);
|
||||
}
|
||||
} else {
|
||||
// No slow path; it's a fast decision.
|
||||
if (L_failure == &L_fallthrough) {
|
||||
brx(Assembler::equal, do_bool_sets, Assembler::pt, *L_success);
|
||||
DELAYED_BOOL_SET(1);
|
||||
BOOL_SET(0);
|
||||
} else {
|
||||
brx(Assembler::notEqual, do_bool_sets, Assembler::pn, *L_failure);
|
||||
DELAYED_BOOL_SET(0);
|
||||
FINAL_JUMP(*L_success, 1);
|
||||
}
|
||||
}
|
||||
|
||||
bind(L_fallthrough);
|
||||
|
||||
#undef final_jump
|
||||
#undef bool_set
|
||||
#undef DELAYED_BOOL_SET
|
||||
#undef final_jump
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::check_klass_subtype_slow_path(Register sub_klass,
|
||||
Register super_klass,
|
||||
Register count_temp,
|
||||
Register scan_temp,
|
||||
Register scratch_reg,
|
||||
Register coop_reg,
|
||||
Label* L_success,
|
||||
Label* L_failure) {
|
||||
assert_different_registers(sub_klass, super_klass,
|
||||
count_temp, scan_temp, scratch_reg, coop_reg);
|
||||
|
||||
Label L_fallthrough, L_loop;
|
||||
int label_nulls = 0;
|
||||
if (L_success == NULL) { L_success = &L_fallthrough; label_nulls++; }
|
||||
if (L_failure == NULL) { L_failure = &L_fallthrough; label_nulls++; }
|
||||
assert(label_nulls <= 1, "at most one NULL in the batch");
|
||||
|
||||
// a couple of useful fields in sub_klass:
|
||||
int ss_offset = (klassOopDesc::header_size() * HeapWordSize +
|
||||
Klass::secondary_supers_offset_in_bytes());
|
||||
int sc_offset = (klassOopDesc::header_size() * HeapWordSize +
|
||||
Klass::secondary_super_cache_offset_in_bytes());
|
||||
|
||||
// Do a linear scan of the secondary super-klass chain.
|
||||
// This code is rarely used, so simplicity is a virtue here.
|
||||
|
||||
#ifndef PRODUCT
|
||||
int* pst_counter = &SharedRuntime::_partial_subtype_ctr;
|
||||
inc_counter((address) pst_counter, count_temp, scan_temp);
|
||||
#endif
|
||||
|
||||
// We will consult the secondary-super array.
|
||||
ld_ptr(sub_klass, ss_offset, scan_temp);
|
||||
|
||||
// Compress superclass if necessary.
|
||||
Register search_key = super_klass;
|
||||
bool decode_super_klass = false;
|
||||
if (UseCompressedOops) {
|
||||
if (coop_reg != noreg) {
|
||||
encode_heap_oop_not_null(super_klass, coop_reg);
|
||||
search_key = coop_reg;
|
||||
} else {
|
||||
encode_heap_oop_not_null(super_klass);
|
||||
decode_super_klass = true; // scarce temps!
|
||||
}
|
||||
// The superclass is never null; it would be a basic system error if a null
|
||||
// pointer were to sneak in here. Note that we have already loaded the
|
||||
// Klass::super_check_offset from the super_klass in the fast path,
|
||||
// so if there is a null in that register, we are already in the afterlife.
|
||||
}
|
||||
|
||||
// Load the array length. (Positive movl does right thing on LP64.)
|
||||
lduw(scan_temp, arrayOopDesc::length_offset_in_bytes(), count_temp);
|
||||
|
||||
// Check for empty secondary super list
|
||||
tst(count_temp);
|
||||
|
||||
// Top of search loop
|
||||
bind(L_loop);
|
||||
br(Assembler::equal, false, Assembler::pn, *L_failure);
|
||||
delayed()->add(scan_temp, heapOopSize, scan_temp);
|
||||
assert(heapOopSize != 0, "heapOopSize should be initialized");
|
||||
|
||||
// Skip the array header in all array accesses.
|
||||
int elem_offset = arrayOopDesc::base_offset_in_bytes(T_OBJECT);
|
||||
elem_offset -= heapOopSize; // the scan pointer was pre-incremented also
|
||||
|
||||
// Load next super to check
|
||||
if (UseCompressedOops) {
|
||||
// Don't use load_heap_oop; we don't want to decode the element.
|
||||
lduw( scan_temp, elem_offset, scratch_reg );
|
||||
} else {
|
||||
ld_ptr( scan_temp, elem_offset, scratch_reg );
|
||||
}
|
||||
|
||||
// Look for Rsuper_klass on Rsub_klass's secondary super-class-overflow list
|
||||
cmp(scratch_reg, search_key);
|
||||
|
||||
// A miss means we are NOT a subtype and need to keep looping
|
||||
brx(Assembler::notEqual, false, Assembler::pn, L_loop);
|
||||
delayed()->deccc(count_temp); // decrement trip counter in delay slot
|
||||
|
||||
// Falling out the bottom means we found a hit; we ARE a subtype
|
||||
if (decode_super_klass) decode_heap_oop(super_klass);
|
||||
|
||||
// Success. Cache the super we found and proceed in triumph.
|
||||
st_ptr(super_klass, sub_klass, sc_offset);
|
||||
|
||||
if (L_success != &L_fallthrough) {
|
||||
ba(false, *L_success);
|
||||
delayed()->nop();
|
||||
}
|
||||
|
||||
bind(L_fallthrough);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void MacroAssembler::biased_locking_enter(Register obj_reg, Register mark_reg,
|
||||
Register temp_reg,
|
||||
Label& done, Label* slow_case,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue