8331341: secondary_super_cache does not scale well: C1 and interpreter

Reviewed-by: vlivanov, kvn, dlong
This commit is contained in:
Andrew Haley 2024-11-06 08:58:08 +00:00
parent 06d8216a4e
commit ead0116f26
22 changed files with 919 additions and 276 deletions

View File

@ -16281,9 +16281,10 @@ instruct ShouldNotReachHere() %{
instruct partialSubtypeCheck(iRegP_R4 sub, iRegP_R0 super, iRegP_R2 temp, iRegP_R5 result, rFlagsReg cr)
%{
match(Set result (PartialSubtypeCheck sub super));
predicate(!UseSecondarySupersTable);
effect(KILL cr, KILL temp);
ins_cost(1100); // slightly larger than the next version
ins_cost(20 * INSN_COST); // slightly larger than the next version
format %{ "partialSubtypeCheck $result, $sub, $super" %}
ins_encode(aarch64_enc_partial_subtype_check(sub, super, temp, result));
@ -16293,6 +16294,34 @@ instruct partialSubtypeCheck(iRegP_R4 sub, iRegP_R0 super, iRegP_R2 temp, iRegP_
ins_pipe(pipe_class_memory);
%}
// Two versions of partialSubtypeCheck, both used when we need to
// search for a super class in the secondary supers array. The first
// is used when we don't know _a priori_ the class being searched
// for. The second, far more common, is used when we do know: this is
// used for instanceof, checkcast, and any case where C2 can determine
// it by constant propagation.
instruct partialSubtypeCheckVarSuper(iRegP_R4 sub, iRegP_R0 super, vRegD_V0 vtemp, iRegP_R5 result,
iRegP_R1 tempR1, iRegP_R2 tempR2, iRegP_R3 tempR3,
rFlagsReg cr)
%{
match(Set result (PartialSubtypeCheck sub super));
predicate(UseSecondarySupersTable);
effect(KILL cr, TEMP tempR1, TEMP tempR2, TEMP tempR3, TEMP vtemp);
ins_cost(10 * INSN_COST); // slightly larger than the next version
format %{ "partialSubtypeCheck $result, $sub, $super" %}
ins_encode %{
__ lookup_secondary_supers_table_var($sub$$Register, $super$$Register,
$tempR1$$Register, $tempR2$$Register, $tempR3$$Register,
$vtemp$$FloatRegister,
$result$$Register, /*L_success*/nullptr);
%}
ins_pipe(pipe_class_memory);
%}
instruct partialSubtypeCheckConstSuper(iRegP_R4 sub, iRegP_R0 super_reg, immP super_con, vRegD_V0 vtemp, iRegP_R5 result,
iRegP_R1 tempR1, iRegP_R2 tempR2, iRegP_R3 tempR3,
rFlagsReg cr)
@ -16301,18 +16330,19 @@ instruct partialSubtypeCheckConstSuper(iRegP_R4 sub, iRegP_R0 super_reg, immP su
predicate(UseSecondarySupersTable);
effect(KILL cr, TEMP tempR1, TEMP tempR2, TEMP tempR3, TEMP vtemp);
ins_cost(700); // smaller than the next version
ins_cost(5 * INSN_COST); // smaller than the next version
format %{ "partialSubtypeCheck $result, $sub, $super_reg, $super_con" %}
ins_encode %{
bool success = false;
u1 super_klass_slot = ((Klass*)$super_con$$constant)->hash_slot();
if (InlineSecondarySupersTest) {
success = __ lookup_secondary_supers_table($sub$$Register, $super_reg$$Register,
$tempR1$$Register, $tempR2$$Register, $tempR3$$Register,
$vtemp$$FloatRegister,
$result$$Register,
super_klass_slot);
success =
__ lookup_secondary_supers_table_const($sub$$Register, $super_reg$$Register,
$tempR1$$Register, $tempR2$$Register, $tempR3$$Register,
$vtemp$$FloatRegister,
$result$$Register,
super_klass_slot);
} else {
address call = __ trampoline_call(RuntimeAddress(StubRoutines::lookup_secondary_supers_table_stub(super_klass_slot)));
success = (call != nullptr);
@ -16326,21 +16356,6 @@ instruct partialSubtypeCheckConstSuper(iRegP_R4 sub, iRegP_R0 super_reg, immP su
ins_pipe(pipe_class_memory);
%}
instruct partialSubtypeCheckVsZero(iRegP_R4 sub, iRegP_R0 super, iRegP_R2 temp, iRegP_R5 result, immP0 zero, rFlagsReg cr)
%{
match(Set cr (CmpP (PartialSubtypeCheck sub super) zero));
effect(KILL temp, KILL result);
ins_cost(1100); // slightly larger than the next version
format %{ "partialSubtypeCheck $result, $sub, $super == 0" %}
ins_encode(aarch64_enc_partial_subtype_check(sub, super, temp, result));
opcode(0x0); // Don't zero result reg on hit
ins_pipe(pipe_class_memory);
%}
// Intrisics for String.compareTo()
instruct string_compareU(iRegP_R1 str1, iRegI_R2 cnt1, iRegP_R3 str2, iRegI_R4 cnt2,

View File

@ -842,7 +842,13 @@ OopMapSet* Runtime1::generate_code_for(C1StubId id, StubAssembler* sasm) {
__ ldp(r4, r0, Address(sp, (sup_k_off) * VMRegImpl::stack_slot_size));
Label miss;
__ check_klass_subtype_slow_path(r4, r0, r2, r5, nullptr, &miss);
__ check_klass_subtype_slow_path(/*sub_klass*/r4,
/*super_klass*/r0,
/*temp_reg*/r2,
/*temp2_reg*/r5,
/*L_success*/nullptr,
/*L_failure*/&miss);
// Need extras for table lookup: r1, r3, vtemp
// fallthrough on success:
__ mov(rscratch1, 1);

View File

@ -1387,13 +1387,10 @@ void MacroAssembler::check_klass_subtype_fast_path(Register sub_klass,
Label* L_success,
Label* L_failure,
Label* L_slow_path,
RegisterOrConstant super_check_offset) {
assert_different_registers(sub_klass, super_klass, temp_reg);
bool must_load_sco = (super_check_offset.constant_or_zero() == -1);
if (super_check_offset.is_register()) {
assert_different_registers(sub_klass, super_klass,
super_check_offset.as_register());
} else if (must_load_sco) {
Register super_check_offset) {
assert_different_registers(sub_klass, super_klass, temp_reg, super_check_offset);
bool must_load_sco = ! super_check_offset->is_valid();
if (must_load_sco) {
assert(temp_reg != noreg, "supply either a temp or a register offset");
}
@ -1404,7 +1401,6 @@ void MacroAssembler::check_klass_subtype_fast_path(Register sub_klass,
if (L_slow_path == nullptr) { L_slow_path = &L_fallthrough; label_nulls++; }
assert(label_nulls <= 1, "at most one null in the batch");
int sc_offset = in_bytes(Klass::secondary_super_cache_offset());
int sco_offset = in_bytes(Klass::super_check_offset_offset());
Address super_check_offset_addr(super_klass, sco_offset);
@ -1426,11 +1422,13 @@ void MacroAssembler::check_klass_subtype_fast_path(Register sub_klass,
// Check the supertype display:
if (must_load_sco) {
ldrw(temp_reg, super_check_offset_addr);
super_check_offset = RegisterOrConstant(temp_reg);
super_check_offset = temp_reg;
}
Address super_check_addr(sub_klass, super_check_offset);
ldr(rscratch1, super_check_addr);
cmp(super_klass, rscratch1); // load displayed supertype
br(Assembler::EQ, *L_success);
// This check has worked decisively for primary supers.
// Secondary supers are sought in the super_cache ('super_cache_addr').
@ -1443,31 +1441,12 @@ void MacroAssembler::check_klass_subtype_fast_path(Register sub_klass,
// 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()) {
br(Assembler::EQ, *L_success);
subs(zr, super_check_offset.as_register(), sc_offset);
if (L_failure == &L_fallthrough) {
br(Assembler::EQ, *L_slow_path);
} else {
br(Assembler::NE, *L_failure);
final_jmp(*L_slow_path);
}
} else if (super_check_offset.as_constant() == sc_offset) {
// Need a slow path; fast failure is impossible.
if (L_slow_path == &L_fallthrough) {
br(Assembler::EQ, *L_success);
} else {
br(Assembler::NE, *L_slow_path);
final_jmp(*L_success);
}
sub(rscratch1, super_check_offset, in_bytes(Klass::secondary_super_cache_offset()));
if (L_failure == &L_fallthrough) {
cbz(rscratch1, *L_slow_path);
} else {
// No slow path; it's a fast decision.
if (L_failure == &L_fallthrough) {
br(Assembler::EQ, *L_success);
} else {
br(Assembler::NE, *L_failure);
final_jmp(*L_success);
}
cbnz(rscratch1, *L_failure);
final_jmp(*L_slow_path);
}
bind(L_fallthrough);
@ -1507,13 +1486,13 @@ void MacroAssembler::repne_scanw(Register addr, Register value, Register count,
bind(Lexit);
}
void MacroAssembler::check_klass_subtype_slow_path(Register sub_klass,
Register super_klass,
Register temp_reg,
Register temp2_reg,
Label* L_success,
Label* L_failure,
bool set_cond_codes) {
void MacroAssembler::check_klass_subtype_slow_path_linear(Register sub_klass,
Register super_klass,
Register temp_reg,
Register temp2_reg,
Label* L_success,
Label* L_failure,
bool set_cond_codes) {
// NB! Callers may assume that, when temp2_reg is a valid register,
// this code sets it to a nonzero value.
@ -1581,7 +1560,10 @@ void MacroAssembler::check_klass_subtype_slow_path(Register sub_klass,
br(Assembler::NE, *L_failure);
// Success. Cache the super we found and proceed in triumph.
str(super_klass, super_cache_addr);
if (UseSecondarySupersCache) {
str(super_klass, super_cache_addr);
}
if (L_success != &L_fallthrough) {
b(*L_success);
@ -1592,6 +1574,102 @@ void MacroAssembler::check_klass_subtype_slow_path(Register sub_klass,
bind(L_fallthrough);
}
// If Register r is invalid, remove a new register from
// available_regs, and add new register to regs_to_push.
Register MacroAssembler::allocate_if_noreg(Register r,
RegSetIterator<Register> &available_regs,
RegSet &regs_to_push) {
if (!r->is_valid()) {
r = *available_regs++;
regs_to_push += r;
}
return r;
}
// check_klass_subtype_slow_path_table() looks for super_klass in the
// hash table belonging to super_klass, branching to L_success or
// L_failure as appropriate. This is essentially a shim which
// allocates registers as necessary then calls
// lookup_secondary_supers_table() to do the work. Any of the temp
// regs may be noreg, in which case this logic will chooses some
// registers push and pop them from the stack.
void MacroAssembler::check_klass_subtype_slow_path_table(Register sub_klass,
Register super_klass,
Register temp_reg,
Register temp2_reg,
Register temp3_reg,
Register result_reg,
FloatRegister vtemp,
Label* L_success,
Label* L_failure,
bool set_cond_codes) {
RegSet temps = RegSet::of(temp_reg, temp2_reg, temp3_reg);
assert_different_registers(sub_klass, super_klass, temp_reg, temp2_reg, rscratch1);
Label L_fallthrough;
int label_nulls = 0;
if (L_success == nullptr) { L_success = &L_fallthrough; label_nulls++; }
if (L_failure == nullptr) { L_failure = &L_fallthrough; label_nulls++; }
assert(label_nulls <= 1, "at most one null in the batch");
BLOCK_COMMENT("check_klass_subtype_slow_path");
RegSetIterator<Register> available_regs
= (RegSet::range(r0, r15) - temps - sub_klass - super_klass).begin();
RegSet pushed_regs;
temp_reg = allocate_if_noreg(temp_reg, available_regs, pushed_regs);
temp2_reg = allocate_if_noreg(temp2_reg, available_regs, pushed_regs);
temp3_reg = allocate_if_noreg(temp3_reg, available_regs, pushed_regs);
result_reg = allocate_if_noreg(result_reg, available_regs, pushed_regs);
push(pushed_regs, sp);
lookup_secondary_supers_table_var(sub_klass,
super_klass,
temp_reg, temp2_reg, temp3_reg, vtemp, result_reg,
nullptr);
cmp(result_reg, zr);
// Unspill the temp. registers:
pop(pushed_regs, sp);
// NB! Callers may assume that, when set_cond_codes is true, this
// code sets temp2_reg to a nonzero value.
if (set_cond_codes) {
mov(temp2_reg, 1);
}
br(Assembler::NE, *L_failure);
if (L_success != &L_fallthrough) {
b(*L_success);
}
bind(L_fallthrough);
}
void MacroAssembler::check_klass_subtype_slow_path(Register sub_klass,
Register super_klass,
Register temp_reg,
Register temp2_reg,
Label* L_success,
Label* L_failure,
bool set_cond_codes) {
if (UseSecondarySupersTable) {
check_klass_subtype_slow_path_table
(sub_klass, super_klass, temp_reg, temp2_reg, /*temp3*/noreg, /*result*/noreg,
/*vtemp*/fnoreg,
L_success, L_failure, set_cond_codes);
} else {
check_klass_subtype_slow_path_linear
(sub_klass, super_klass, temp_reg, temp2_reg, L_success, L_failure, set_cond_codes);
}
}
// Ensure that the inline code and the stub are using the same registers.
#define LOOKUP_SECONDARY_SUPERS_TABLE_REGISTERS \
do { \
@ -1604,16 +1682,15 @@ do { \
(result == r5 || result == noreg), "registers must match aarch64.ad"); \
} while(0)
// Return true: we succeeded in generating this code
bool MacroAssembler::lookup_secondary_supers_table(Register r_sub_klass,
Register r_super_klass,
Register temp1,
Register temp2,
Register temp3,
FloatRegister vtemp,
Register result,
u1 super_klass_slot,
bool stub_is_near) {
bool MacroAssembler::lookup_secondary_supers_table_const(Register r_sub_klass,
Register r_super_klass,
Register temp1,
Register temp2,
Register temp3,
FloatRegister vtemp,
Register result,
u1 super_klass_slot,
bool stub_is_near) {
assert_different_registers(r_sub_klass, temp1, temp2, temp3, result, rscratch1, rscratch2);
Label L_fallthrough;
@ -1635,9 +1712,9 @@ bool MacroAssembler::lookup_secondary_supers_table(Register r_sub_klass,
// We're going to need the bitmap in a vector reg and in a core reg,
// so load both now.
ldr(r_bitmap, Address(r_sub_klass, Klass::bitmap_offset()));
ldr(r_bitmap, Address(r_sub_klass, Klass::secondary_supers_bitmap_offset()));
if (bit != 0) {
ldrd(vtemp, Address(r_sub_klass, Klass::bitmap_offset()));
ldrd(vtemp, Address(r_sub_klass, Klass::secondary_supers_bitmap_offset()));
}
// First check the bitmap to see if super_klass might be present. If
// the bit is zero, we are certain that super_klass is not one of
@ -1701,6 +1778,113 @@ bool MacroAssembler::lookup_secondary_supers_table(Register r_sub_klass,
return true;
}
// At runtime, return 0 in result if r_super_klass is a superclass of
// r_sub_klass, otherwise return nonzero. Use this version of
// lookup_secondary_supers_table() if you don't know ahead of time
// which superclass will be searched for. Used by interpreter and
// runtime stubs. It is larger and has somewhat greater latency than
// the version above, which takes a constant super_klass_slot.
void MacroAssembler::lookup_secondary_supers_table_var(Register r_sub_klass,
Register r_super_klass,
Register temp1,
Register temp2,
Register temp3,
FloatRegister vtemp,
Register result,
Label *L_success) {
assert_different_registers(r_sub_klass, temp1, temp2, temp3, result, rscratch1, rscratch2);
Label L_fallthrough;
BLOCK_COMMENT("lookup_secondary_supers_table {");
const Register
r_array_index = temp3,
slot = rscratch1,
r_bitmap = rscratch2;
ldrb(slot, Address(r_super_klass, Klass::hash_slot_offset()));
// Make sure that result is nonzero if the test below misses.
mov(result, 1);
ldr(r_bitmap, Address(r_sub_klass, Klass::secondary_supers_bitmap_offset()));
// First check the bitmap to see if super_klass might be present. If
// the bit is zero, we are certain that super_klass is not one of
// the secondary supers.
// This next instruction is equivalent to:
// mov(tmp_reg, (u1)(Klass::SECONDARY_SUPERS_TABLE_SIZE - 1));
// sub(temp2, tmp_reg, slot);
eor(temp2, slot, (u1)(Klass::SECONDARY_SUPERS_TABLE_SIZE - 1));
lslv(temp2, r_bitmap, temp2);
tbz(temp2, Klass::SECONDARY_SUPERS_TABLE_SIZE - 1, L_fallthrough);
bool must_save_v0 = (vtemp == fnoreg);
if (must_save_v0) {
// temp1 and result are free, so use them to preserve vtemp
vtemp = v0;
mov(temp1, vtemp, D, 0);
mov(result, vtemp, D, 1);
}
// Get the first array index that can contain super_klass into r_array_index.
mov(vtemp, D, 0, temp2);
cnt(vtemp, T8B, vtemp);
addv(vtemp, T8B, vtemp);
mov(r_array_index, vtemp, D, 0);
if (must_save_v0) {
mov(vtemp, D, 0, temp1 );
mov(vtemp, D, 1, result);
}
// NB! r_array_index is off by 1. It is compensated by keeping r_array_base off by 1 word.
const Register
r_array_base = temp1,
r_array_length = temp2;
// The value i in r_array_index is >= 1, so even though r_array_base
// points to the length, we don't need to adjust it to point to the
// data.
assert(Array<Klass*>::base_offset_in_bytes() == wordSize, "Adjust this code");
assert(Array<Klass*>::length_offset_in_bytes() == 0, "Adjust this code");
// We will consult the secondary-super array.
ldr(r_array_base, Address(r_sub_klass, in_bytes(Klass::secondary_supers_offset())));
ldr(result, Address(r_array_base, r_array_index, Address::lsl(LogBytesPerWord)));
eor(result, result, r_super_klass);
cbz(result, L_success ? *L_success : L_fallthrough); // Found a match
// Is there another entry to check? Consult the bitmap.
rorv(r_bitmap, r_bitmap, slot);
// rol(r_bitmap, r_bitmap, 1);
tbz(r_bitmap, 1, L_fallthrough);
// The slot we just inspected is at secondary_supers[r_array_index - 1].
// The next slot to be inspected, by the logic we're about to call,
// is secondary_supers[r_array_index]. Bits 0 and 1 in the bitmap
// have been checked.
lookup_secondary_supers_table_slow_path(r_super_klass, r_array_base, r_array_index,
r_bitmap, r_array_length, result, /*is_stub*/false);
BLOCK_COMMENT("} lookup_secondary_supers_table");
bind(L_fallthrough);
if (VerifySecondarySupers) {
verify_secondary_supers_table(r_sub_klass, r_super_klass, // r4, r0
temp1, temp2, result); // r1, r2, r5
}
if (L_success) {
cbz(result, *L_success);
}
}
// Called by code generated by check_klass_subtype_slow_path
// above. This is called when there is a collision in the hashed
// lookup in the secondary supers array.
@ -1709,14 +1893,17 @@ void MacroAssembler::lookup_secondary_supers_table_slow_path(Register r_super_kl
Register r_array_index,
Register r_bitmap,
Register temp1,
Register result) {
Register result,
bool is_stub) {
assert_different_registers(r_super_klass, r_array_base, r_array_index, r_bitmap, temp1, result, rscratch1);
const Register
r_array_length = temp1,
r_sub_klass = noreg; // unused
LOOKUP_SECONDARY_SUPERS_TABLE_REGISTERS;
if (is_stub) {
LOOKUP_SECONDARY_SUPERS_TABLE_REGISTERS;
}
Label L_fallthrough, L_huge;
@ -1741,8 +1928,10 @@ void MacroAssembler::lookup_secondary_supers_table_slow_path(Register r_super_kl
{ // This is conventional linear probing, but instead of terminating
// when a null entry is found in the table, we maintain a bitmap
// in which a 0 indicates missing entries.
// The check above guarantees there are 0s in the bitmap, so the loop
// eventually terminates.
// As long as the bitmap is not completely full,
// array_length == popcount(bitmap). The array_length check above
// guarantees there are 0s in the bitmap, so the loop eventually
// terminates.
Label L_loop;
bind(L_loop);
@ -1788,8 +1977,6 @@ void MacroAssembler::verify_secondary_supers_table(Register r_sub_klass,
r_array_index = noreg, // unused
r_bitmap = noreg; // unused
LOOKUP_SECONDARY_SUPERS_TABLE_REGISTERS;
BLOCK_COMMENT("verify_secondary_supers_table {");
// We will consult the secondary-super array.

View File

@ -995,7 +995,7 @@ public:
Label* L_success,
Label* L_failure,
Label* L_slow_path,
RegisterOrConstant super_check_offset = RegisterOrConstant(-1));
Register super_check_offset = noreg);
// The rest of the type check; must be wired to a corresponding fast path.
// It does not repeat the fast path logic, so don't use it standalone.
@ -1010,17 +1010,54 @@ public:
Label* L_failure,
bool set_cond_codes = false);
void check_klass_subtype_slow_path_linear(Register sub_klass,
Register super_klass,
Register temp_reg,
Register temp2_reg,
Label* L_success,
Label* L_failure,
bool set_cond_codes = false);
void check_klass_subtype_slow_path_table(Register sub_klass,
Register super_klass,
Register temp_reg,
Register temp2_reg,
Register temp3_reg,
Register result_reg,
FloatRegister vtemp_reg,
Label* L_success,
Label* L_failure,
bool set_cond_codes = false);
// If r is valid, return r.
// If r is invalid, remove a register r2 from available_regs, add r2
// to regs_to_push, then return r2.
Register allocate_if_noreg(const Register r,
RegSetIterator<Register> &available_regs,
RegSet &regs_to_push);
// Secondary subtype checking
void lookup_secondary_supers_table_var(Register sub_klass,
Register r_super_klass,
Register temp1,
Register temp2,
Register temp3,
FloatRegister vtemp,
Register result,
Label *L_success);
// As above, but with a constant super_klass.
// The result is in Register result, not the condition codes.
bool lookup_secondary_supers_table(Register r_sub_klass,
Register r_super_klass,
Register temp1,
Register temp2,
Register temp3,
FloatRegister vtemp,
Register result,
u1 super_klass_slot,
bool stub_is_near = false);
bool lookup_secondary_supers_table_const(Register r_sub_klass,
Register r_super_klass,
Register temp1,
Register temp2,
Register temp3,
FloatRegister vtemp,
Register result,
u1 super_klass_slot,
bool stub_is_near = false);
void verify_secondary_supers_table(Register r_sub_klass,
Register r_super_klass,
@ -1033,7 +1070,8 @@ public:
Register r_array_index,
Register r_bitmap,
Register temp1,
Register result);
Register result,
bool is_stub = true);
// Simplified, combined version, good for typical uses.
// Falls through on failure.

View File

@ -1842,6 +1842,9 @@ class StubGenerator: public StubCodeGenerator {
void generate_type_check(Register sub_klass,
Register super_check_offset,
Register super_klass,
Register temp1,
Register temp2,
Register result,
Label& L_success) {
assert_different_registers(sub_klass, super_check_offset, super_klass);
@ -1851,7 +1854,7 @@ class StubGenerator: public StubCodeGenerator {
__ check_klass_subtype_fast_path(sub_klass, super_klass, noreg, &L_success, &L_miss, nullptr,
super_check_offset);
__ check_klass_subtype_slow_path(sub_klass, super_klass, noreg, noreg, &L_success, nullptr);
__ check_klass_subtype_slow_path(sub_klass, super_klass, temp1, temp2, &L_success, nullptr);
// Fall through on failure!
__ BIND(L_miss);
@ -1987,7 +1990,17 @@ class StubGenerator: public StubCodeGenerator {
__ cbz(copied_oop, L_store_element);
__ load_klass(r19_klass, copied_oop);// query the object klass
generate_type_check(r19_klass, ckoff, ckval, L_store_element);
BLOCK_COMMENT("type_check:");
generate_type_check(/*sub_klass*/r19_klass,
/*super_check_offset*/ckoff,
/*super_klass*/ckval,
/*r_array_base*/gct1,
/*temp2*/gct2,
/*result*/r10, L_store_element);
// Fall through on failure!
// ======== end loop ========
// It was a real error; we must depend on the caller to finish the job.
@ -1996,7 +2009,7 @@ class StubGenerator: public StubCodeGenerator {
// their number to the caller.
__ subs(count, count_save, count); // K = partially copied oop count
__ eon(count, count, zr); // report (-1^K) to caller
__ eon(count, count, zr); // report (-1^K) to caller
__ br(Assembler::EQ, L_done_pop);
__ BIND(L_do_card_marks);
@ -2363,7 +2376,8 @@ class StubGenerator: public StubCodeGenerator {
__ ldrw(sco_temp, Address(dst_klass, sco_offset));
// Smashes rscratch1, rscratch2
generate_type_check(scratch_src_klass, sco_temp, dst_klass, L_plain_copy);
generate_type_check(scratch_src_klass, sco_temp, dst_klass, /*temps*/ noreg, noreg, noreg,
L_plain_copy);
// Fetch destination element klass from the ObjArrayKlass header.
int ek_offset = in_bytes(ObjArrayKlass::element_klass_offset());
@ -7097,10 +7111,10 @@ class StubGenerator: public StubCodeGenerator {
Label L_success;
__ enter();
__ lookup_secondary_supers_table(r_sub_klass, r_super_klass,
r_array_base, r_array_length, r_array_index,
vtemp, result, super_klass_index,
/*stub_is_near*/true);
__ lookup_secondary_supers_table_const(r_sub_klass, r_super_klass,
r_array_base, r_array_length, r_array_index,
vtemp, result, super_klass_index,
/*stub_is_near*/true);
__ leave();
__ ret(lr);

View File

@ -2187,7 +2187,7 @@ void MacroAssembler::lookup_secondary_supers_table(Register r_sub_klass,
LOOKUP_SECONDARY_SUPERS_TABLE_REGISTERS;
ld(r_bitmap, in_bytes(Klass::bitmap_offset()), r_sub_klass);
ld(r_bitmap, in_bytes(Klass::secondary_supers_bitmap_offset()), r_sub_klass);
// First check the bitmap to see if super_klass might be present. If
// the bit is zero, we are certain that super_klass is not one of

View File

@ -3928,7 +3928,7 @@ bool MacroAssembler::lookup_secondary_supers_table(Register r_sub_klass,
// Initialize result value to 1 which means mismatch.
mv(result, 1);
ld(r_bitmap, Address(r_sub_klass, Klass::bitmap_offset()));
ld(r_bitmap, Address(r_sub_klass, Klass::secondary_supers_bitmap_offset()));
// First check the bitmap to see if super_klass might be present. If
// the bit is zero, we are certain that super_klass is not one of

View File

@ -3215,7 +3215,7 @@ void MacroAssembler::lookup_secondary_supers_table(Register r_sub_klass,
LOOKUP_SECONDARY_SUPERS_TABLE_REGISTERS;
z_lg(r_bitmap, Address(r_sub_klass, Klass::bitmap_offset()));
z_lg(r_bitmap, Address(r_sub_klass, Klass::secondary_supers_bitmap_offset()));
// First check the bitmap to see if super_klass might be present. If
// the bit is zero, we are certain that super_klass is not one of

View File

@ -4660,13 +4660,13 @@ void MacroAssembler::check_klass_subtype_fast_path(Register sub_klass,
}
void MacroAssembler::check_klass_subtype_slow_path(Register sub_klass,
Register super_klass,
Register temp_reg,
Register temp2_reg,
Label* L_success,
Label* L_failure,
bool set_cond_codes) {
void MacroAssembler::check_klass_subtype_slow_path_linear(Register sub_klass,
Register super_klass,
Register temp_reg,
Register temp2_reg,
Label* L_success,
Label* L_failure,
bool set_cond_codes) {
assert_different_registers(sub_klass, super_klass, temp_reg);
if (temp2_reg != noreg)
assert_different_registers(sub_klass, super_klass, temp_reg, temp2_reg);
@ -4752,7 +4752,128 @@ void MacroAssembler::check_klass_subtype_slow_path(Register sub_klass,
bind(L_fallthrough);
}
#ifdef _LP64
#ifndef _LP64
// 32-bit x86 only: always use the linear search.
void MacroAssembler::check_klass_subtype_slow_path(Register sub_klass,
Register super_klass,
Register temp_reg,
Register temp2_reg,
Label* L_success,
Label* L_failure,
bool set_cond_codes) {
check_klass_subtype_slow_path_linear
(sub_klass, super_klass, temp_reg, temp2_reg, L_success, L_failure, set_cond_codes);
}
#else // _LP64
void MacroAssembler::check_klass_subtype_slow_path(Register sub_klass,
Register super_klass,
Register temp_reg,
Register temp2_reg,
Label* L_success,
Label* L_failure,
bool set_cond_codes) {
assert(set_cond_codes == false, "must be false on 64-bit x86");
check_klass_subtype_slow_path
(sub_klass, super_klass, temp_reg, temp2_reg, noreg, noreg,
L_success, L_failure);
}
void MacroAssembler::check_klass_subtype_slow_path(Register sub_klass,
Register super_klass,
Register temp_reg,
Register temp2_reg,
Register temp3_reg,
Register temp4_reg,
Label* L_success,
Label* L_failure) {
if (UseSecondarySupersTable) {
check_klass_subtype_slow_path_table
(sub_klass, super_klass, temp_reg, temp2_reg, temp3_reg, temp4_reg,
L_success, L_failure);
} else {
check_klass_subtype_slow_path_linear
(sub_klass, super_klass, temp_reg, temp2_reg, L_success, L_failure, /*set_cond_codes*/false);
}
}
Register MacroAssembler::allocate_if_noreg(Register r,
RegSetIterator<Register> &available_regs,
RegSet &regs_to_push) {
if (!r->is_valid()) {
r = *available_regs++;
regs_to_push += r;
}
return r;
}
void MacroAssembler::check_klass_subtype_slow_path_table(Register sub_klass,
Register super_klass,
Register temp_reg,
Register temp2_reg,
Register temp3_reg,
Register result_reg,
Label* L_success,
Label* L_failure) {
// NB! Callers may assume that, when temp2_reg is a valid register,
// this code sets it to a nonzero value.
bool temp2_reg_was_valid = temp2_reg->is_valid();
RegSet temps = RegSet::of(temp_reg, temp2_reg, temp3_reg);
Label L_fallthrough;
int label_nulls = 0;
if (L_success == nullptr) { L_success = &L_fallthrough; label_nulls++; }
if (L_failure == nullptr) { L_failure = &L_fallthrough; label_nulls++; }
assert(label_nulls <= 1, "at most one null in the batch");
BLOCK_COMMENT("check_klass_subtype_slow_path_table");
RegSetIterator<Register> available_regs
= (RegSet::of(rax, rcx, rdx, r8) + r9 + r10 + r11 + r12 - temps - sub_klass - super_klass).begin();
RegSet pushed_regs;
temp_reg = allocate_if_noreg(temp_reg, available_regs, pushed_regs);
temp2_reg = allocate_if_noreg(temp2_reg, available_regs, pushed_regs);
temp3_reg = allocate_if_noreg(temp3_reg, available_regs, pushed_regs);
result_reg = allocate_if_noreg(result_reg, available_regs, pushed_regs);
Register temp4_reg = allocate_if_noreg(noreg, available_regs, pushed_regs);
assert_different_registers(sub_klass, super_klass, temp_reg, temp2_reg, temp3_reg, result_reg);
{
int register_push_size = pushed_regs.size() * Register::max_slots_per_register * VMRegImpl::stack_slot_size;
int aligned_size = align_up(register_push_size, StackAlignmentInBytes);
subptr(rsp, aligned_size);
push_set(pushed_regs, 0);
lookup_secondary_supers_table_var(sub_klass,
super_klass,
temp_reg, temp2_reg, temp3_reg, temp4_reg, result_reg);
cmpq(result_reg, 0);
// Unspill the temp. registers:
pop_set(pushed_regs, 0);
// Increment SP but do not clobber flags.
lea(rsp, Address(rsp, aligned_size));
}
if (temp2_reg_was_valid) {
movq(temp2_reg, 1);
}
jcc(Assembler::notEqual, *L_failure);
if (L_success != &L_fallthrough) {
jmp(*L_success);
}
bind(L_fallthrough);
}
// population_count variant for running without the POPCNT
// instruction, which was introduced with SSE4.2 in 2008.
@ -4799,14 +4920,44 @@ do { \
assert(result == rdi || result == noreg, "mismatch"); \
} while(0)
void MacroAssembler::lookup_secondary_supers_table(Register r_sub_klass,
Register r_super_klass,
Register temp1,
Register temp2,
Register temp3,
Register temp4,
Register result,
u1 super_klass_slot) {
// Versions of salq and rorq that don't need count to be in rcx
void MacroAssembler::salq(Register dest, Register count) {
if (count == rcx) {
Assembler::salq(dest);
} else {
assert_different_registers(rcx, dest);
xchgq(rcx, count);
Assembler::salq(dest);
xchgq(rcx, count);
}
}
void MacroAssembler::rorq(Register dest, Register count) {
if (count == rcx) {
Assembler::rorq(dest);
} else {
assert_different_registers(rcx, dest);
xchgq(rcx, count);
Assembler::rorq(dest);
xchgq(rcx, count);
}
}
// Return true: we succeeded in generating this code
//
// At runtime, return 0 in result if r_super_klass is a superclass of
// r_sub_klass, otherwise return nonzero. Use this if you know the
// super_klass_slot of the class you're looking for. This is always
// the case for instanceof and checkcast.
void MacroAssembler::lookup_secondary_supers_table_const(Register r_sub_klass,
Register r_super_klass,
Register temp1,
Register temp2,
Register temp3,
Register temp4,
Register result,
u1 super_klass_slot) {
assert_different_registers(r_sub_klass, r_super_klass, temp1, temp2, temp3, temp4, result);
Label L_fallthrough, L_success, L_failure;
@ -4823,7 +4974,7 @@ void MacroAssembler::lookup_secondary_supers_table(Register r_sub_klass,
xorq(result, result); // = 0
movq(r_bitmap, Address(r_sub_klass, Klass::bitmap_offset()));
movq(r_bitmap, Address(r_sub_klass, Klass::secondary_supers_bitmap_offset()));
movq(r_array_index, r_bitmap);
// First check the bitmap to see if super_klass might be present. If
@ -4896,6 +5047,122 @@ void MacroAssembler::lookup_secondary_supers_table(Register r_sub_klass,
}
}
// At runtime, return 0 in result if r_super_klass is a superclass of
// r_sub_klass, otherwise return nonzero. Use this version of
// lookup_secondary_supers_table() if you don't know ahead of time
// which superclass will be searched for. Used by interpreter and
// runtime stubs. It is larger and has somewhat greater latency than
// the version above, which takes a constant super_klass_slot.
void MacroAssembler::lookup_secondary_supers_table_var(Register r_sub_klass,
Register r_super_klass,
Register temp1,
Register temp2,
Register temp3,
Register temp4,
Register result) {
assert_different_registers(r_sub_klass, r_super_klass, temp1, temp2, temp3, temp4, result);
assert_different_registers(r_sub_klass, r_super_klass, rcx);
RegSet temps = RegSet::of(temp1, temp2, temp3, temp4);
Label L_fallthrough, L_success, L_failure;
BLOCK_COMMENT("lookup_secondary_supers_table {");
RegSetIterator<Register> available_regs = (temps - rcx).begin();
// FIXME. Once we are sure that all paths reaching this point really
// do pass rcx as one of our temps we can get rid of the following
// workaround.
assert(temps.contains(rcx), "fix this code");
// We prefer to have our shift count in rcx. If rcx is one of our
// temps, use it for slot. If not, pick any of our temps.
Register slot;
if (!temps.contains(rcx)) {
slot = *available_regs++;
} else {
slot = rcx;
}
const Register r_array_index = *available_regs++;
const Register r_bitmap = *available_regs++;
// The logic above guarantees this property, but we state it here.
assert_different_registers(r_array_index, r_bitmap, rcx);
movq(r_bitmap, Address(r_sub_klass, Klass::secondary_supers_bitmap_offset()));
movq(r_array_index, r_bitmap);
// First check the bitmap to see if super_klass might be present. If
// the bit is zero, we are certain that super_klass is not one of
// the secondary supers.
movb(slot, Address(r_super_klass, Klass::hash_slot_offset()));
xorl(slot, (u1)(Klass::SECONDARY_SUPERS_TABLE_SIZE - 1)); // slot ^ 63 === 63 - slot (mod 64)
salq(r_array_index, slot);
testq(r_array_index, r_array_index);
// We test the MSB of r_array_index, i.e. its sign bit
jcc(Assembler::positive, L_failure);
const Register r_array_base = *available_regs++;
// Get the first array index that can contain super_klass into r_array_index.
population_count(r_array_index, r_array_index, /*temp2*/r_array_base, /*temp3*/slot);
// NB! r_array_index is off by 1. It is compensated by keeping r_array_base off by 1 word.
// We will consult the secondary-super array.
movptr(r_array_base, Address(r_sub_klass, in_bytes(Klass::secondary_supers_offset())));
// We're asserting that the first word in an Array<Klass*> is the
// length, and the second word is the first word of the data. If
// that ever changes, r_array_base will have to be adjusted here.
assert(Array<Klass*>::base_offset_in_bytes() == wordSize, "Adjust this code");
assert(Array<Klass*>::length_offset_in_bytes() == 0, "Adjust this code");
cmpq(r_super_klass, Address(r_array_base, r_array_index, Address::times_8));
jccb(Assembler::equal, L_success);
// Restore slot to its true value
xorl(slot, (u1)(Klass::SECONDARY_SUPERS_TABLE_SIZE - 1)); // slot ^ 63 === 63 - slot (mod 64)
// Linear probe. Rotate the bitmap so that the next bit to test is
// in Bit 1.
rorq(r_bitmap, slot);
// Is there another entry to check? Consult the bitmap.
btq(r_bitmap, 1);
jccb(Assembler::carryClear, L_failure);
// Calls into the stub generated by lookup_secondary_supers_table_slow_path.
// Arguments: r_super_klass, r_array_base, r_array_index, r_bitmap.
// Kills: r_array_length.
// Returns: result.
lookup_secondary_supers_table_slow_path(r_super_klass,
r_array_base,
r_array_index,
r_bitmap,
/*temp1*/result,
/*temp2*/slot,
&L_success,
nullptr);
bind(L_failure);
movq(result, 1);
jmpb(L_fallthrough);
bind(L_success);
xorq(result, result); // = 0
bind(L_fallthrough);
BLOCK_COMMENT("} lookup_secondary_supers_table");
if (VerifySecondarySupers) {
verify_secondary_supers_table(r_sub_klass, r_super_klass, result,
temp1, temp2, temp3);
}
}
void MacroAssembler::repne_scanq(Register addr, Register value, Register count, Register limit,
Label* L_success, Label* L_failure) {
Label L_loop, L_fallthrough;
@ -4936,8 +5203,6 @@ void MacroAssembler::lookup_secondary_supers_table_slow_path(Register r_super_kl
r_sub_klass = noreg,
result = noreg;
LOOKUP_SECONDARY_SUPERS_TABLE_REGISTERS;
Label L_fallthrough;
int label_nulls = 0;
if (L_success == nullptr) { L_success = &L_fallthrough; label_nulls++; }
@ -5034,8 +5299,6 @@ void MacroAssembler::verify_secondary_supers_table(Register r_sub_klass,
r_array_base = temp3,
r_bitmap = noreg;
LOOKUP_SECONDARY_SUPERS_TABLE_REGISTERS;
BLOCK_COMMENT("verify_secondary_supers_table {");
Label L_success, L_failure, L_check, L_done;

View File

@ -646,24 +646,66 @@ public:
Label* L_success,
Label* L_failure,
bool set_cond_codes = false);
void hashed_check_klass_subtype_slow_path(Register sub_klass,
#ifdef _LP64
// The 64-bit version, which may do a hashed subclass lookup.
void check_klass_subtype_slow_path(Register sub_klass,
Register super_klass,
Register temp_reg,
Register temp2_reg,
Register temp3_reg,
Register temp4_reg,
Label* L_success,
Label* L_failure,
bool set_cond_codes = false);
Label* L_failure);
#endif
// Three parts of a hashed subclass lookup: a simple linear search,
// a table lookup, and a fallback that does linear probing in the
// event of a hash collision.
void check_klass_subtype_slow_path_linear(Register sub_klass,
Register super_klass,
Register temp_reg,
Register temp2_reg,
Label* L_success,
Label* L_failure,
bool set_cond_codes = false);
void check_klass_subtype_slow_path_table(Register sub_klass,
Register super_klass,
Register temp_reg,
Register temp2_reg,
Register temp3_reg,
Register result_reg,
Label* L_success,
Label* L_failure);
void hashed_check_klass_subtype_slow_path(Register sub_klass,
Register super_klass,
Register temp_reg,
Label* L_success,
Label* L_failure);
// As above, but with a constant super_klass.
// The result is in Register result, not the condition codes.
void lookup_secondary_supers_table(Register sub_klass,
Register super_klass,
Register temp1,
Register temp2,
Register temp3,
Register temp4,
Register result,
u1 super_klass_slot);
void lookup_secondary_supers_table_const(Register sub_klass,
Register super_klass,
Register temp1,
Register temp2,
Register temp3,
Register temp4,
Register result,
u1 super_klass_slot);
#ifdef _LP64
using Assembler::salq;
void salq(Register dest, Register count);
using Assembler::rorq;
void rorq(Register dest, Register count);
void lookup_secondary_supers_table_var(Register sub_klass,
Register super_klass,
Register temp1,
Register temp2,
Register temp3,
Register temp4,
Register result);
void lookup_secondary_supers_table_slow_path(Register r_super_klass,
Register r_array_base,
@ -680,12 +722,20 @@ public:
Register temp1,
Register temp2,
Register temp3);
#endif
void repne_scanq(Register addr, Register value, Register count, Register limit,
Label* L_success,
Label* L_failure = nullptr);
// Simplified, combined version, good for typical uses.
// If r is valid, return r.
// If r is invalid, remove a register r2 from available_regs, add r2
// to regs_to_push, then return r2.
Register allocate_if_noreg(const Register r,
RegSetIterator<Register> &available_regs,
RegSet &regs_to_push);
// Simplified, combined version, good for typical uses.
// Falls through on failure.
void check_klass_subtype(Register sub_klass,
Register super_klass,

View File

@ -3831,10 +3831,10 @@ address StubGenerator::generate_lookup_secondary_supers_table_stub(u1 super_klas
r_sub_klass = rsi,
result = rdi;
__ lookup_secondary_supers_table(r_sub_klass, r_super_klass,
rdx, rcx, rbx, r11, // temps
result,
super_klass_index);
__ lookup_secondary_supers_table_const(r_sub_klass, r_super_klass,
rdx, rcx, rbx, r11, // temps
result,
super_klass_index);
__ ret(0);
return start;

View File

@ -1717,7 +1717,9 @@ encode %{
Register Resi = as_Register(ESI_enc); // sub class
Label miss;
__ check_klass_subtype_slow_path(Resi, Reax, Recx, Redi,
// NB: Callers may assume that, when $result is a valid register,
// check_klass_subtype_slow_path sets it to a nonzero value.
__ check_klass_subtype_slow_path(Resi, Reax, Recx, Redi,
nullptr, &miss,
/*set_cond_codes:*/ true);
if ($primary) {

View File

@ -1833,24 +1833,6 @@ encode %{
__ bind(done);
%}
enc_class enc_PartialSubtypeCheck()
%{
Register Rrdi = as_Register(RDI_enc); // result register
Register Rrax = as_Register(RAX_enc); // super class
Register Rrcx = as_Register(RCX_enc); // killed
Register Rrsi = as_Register(RSI_enc); // sub class
Label miss;
const bool set_cond_codes = true;
__ check_klass_subtype_slow_path(Rrsi, Rrax, Rrcx, Rrdi,
nullptr, &miss,
/*set_cond_codes:*/ true);
if ($primary) {
__ xorptr(Rrdi, Rrdi);
}
__ bind(miss);
%}
enc_class clear_avx %{
debug_only(int off0 = __ offset());
if (generate_vzeroupper(Compile::current())) {
@ -12141,6 +12123,7 @@ instruct partialSubtypeCheck(rdi_RegP result,
rFlagsReg cr)
%{
match(Set result (PartialSubtypeCheck sub super));
predicate(!UseSecondarySupersTable);
effect(KILL rcx, KILL cr);
ins_cost(1100); // slightly larger than the next version
@ -12153,8 +12136,46 @@ instruct partialSubtypeCheck(rdi_RegP result,
"xorq $result, $result\t\t Hit: rdi zero\n\t"
"miss:\t" %}
opcode(0x1); // Force a XOR of RDI
ins_encode(enc_PartialSubtypeCheck());
ins_encode %{
Label miss;
// NB: Callers may assume that, when $result is a valid register,
// check_klass_subtype_slow_path_linear sets it to a nonzero
// value.
__ check_klass_subtype_slow_path_linear($sub$$Register, $super$$Register,
$rcx$$Register, $result$$Register,
nullptr, &miss,
/*set_cond_codes:*/ true);
__ xorptr($result$$Register, $result$$Register);
__ bind(miss);
%}
ins_pipe(pipe_slow);
%}
// ============================================================================
// Two versions of hashtable-based partialSubtypeCheck, both used when
// we need to search for a super class in the secondary supers array.
// The first is used when we don't know _a priori_ the class being
// searched for. The second, far more common, is used when we do know:
// this is used for instanceof, checkcast, and any case where C2 can
// determine it by constant propagation.
instruct partialSubtypeCheckVarSuper(rsi_RegP sub, rax_RegP super, rdi_RegP result,
rdx_RegL temp1, rcx_RegL temp2, rbx_RegP temp3, r11_RegL temp4,
rFlagsReg cr)
%{
match(Set result (PartialSubtypeCheck sub super));
predicate(UseSecondarySupersTable);
effect(KILL cr, TEMP temp1, TEMP temp2, TEMP temp3, TEMP temp4);
ins_cost(1000);
format %{ "partialSubtypeCheck $result, $sub, $super" %}
ins_encode %{
__ lookup_secondary_supers_table_var($sub$$Register, $super$$Register, $temp1$$Register, $temp2$$Register,
$temp3$$Register, $temp4$$Register, $result$$Register);
%}
ins_pipe(pipe_slow);
%}
@ -12172,7 +12193,7 @@ instruct partialSubtypeCheckConstSuper(rsi_RegP sub, rax_RegP super_reg, immP su
ins_encode %{
u1 super_klass_slot = ((Klass*)$super_con$$constant)->hash_slot();
if (InlineSecondarySupersTest) {
__ lookup_secondary_supers_table($sub$$Register, $super_reg$$Register, $temp1$$Register, $temp2$$Register,
__ lookup_secondary_supers_table_const($sub$$Register, $super_reg$$Register, $temp1$$Register, $temp2$$Register,
$temp3$$Register, $temp4$$Register, $result$$Register,
super_klass_slot);
} else {
@ -12183,28 +12204,6 @@ instruct partialSubtypeCheckConstSuper(rsi_RegP sub, rax_RegP super_reg, immP su
ins_pipe(pipe_slow);
%}
instruct partialSubtypeCheck_vs_Zero(rFlagsReg cr,
rsi_RegP sub, rax_RegP super, rcx_RegI rcx,
immP0 zero,
rdi_RegP result)
%{
match(Set cr (CmpP (PartialSubtypeCheck sub super) zero));
effect(KILL rcx, KILL result);
ins_cost(1000);
format %{ "movq rdi, [$sub + in_bytes(Klass::secondary_supers_offset())]\n\t"
"movl rcx, [rdi + Array<Klass*>::length_offset_in_bytes()]\t# length to scan\n\t"
"addq rdi, Array<Klass*>::base_offset_in_bytes()\t# Skip to start of data; set NZ in case count is zero\n\t"
"repne scasq\t# Scan *rdi++ for a match with rax while cx-- != 0\n\t"
"jne,s miss\t\t# Missed: flags nz\n\t"
"movq [$sub + in_bytes(Klass::secondary_super_cache_offset())], $super\t# Hit: update cache\n\t"
"miss:\t" %}
opcode(0x0); // No need to XOR RDI
ins_encode(enc_PartialSubtypeCheck());
ins_pipe(pipe_slow);
%}
// ============================================================================
// Branch Instructions -- short offset versions
//

View File

@ -196,6 +196,12 @@ public:
return *this;
}
RegSetIterator operator++(int) {
RegSetIterator r = *this;
++(*this);
return r;
}
RegSetIterator<RegImpl>& operator=(const RegSetIterator<RegImpl>& mit) {
_regs= mit._regs;
return *this;

View File

@ -211,7 +211,6 @@ void FileMapHeader::populate(FileMapInfo *info, size_t core_region_alignment,
}
_compressed_oops = UseCompressedOops;
_compressed_class_ptrs = UseCompressedClassPointers;
_use_secondary_supers_table = UseSecondarySupersTable;
_max_heap_size = MaxHeapSize;
_use_optimized_module_handling = CDSConfig::is_using_optimized_module_handling();
_has_full_module_graph = CDSConfig::is_dumping_full_module_graph();
@ -274,7 +273,6 @@ void FileMapHeader::print(outputStream* st) {
st->print_cr("- narrow_oop_mode: %d", _narrow_oop_mode);
st->print_cr("- compressed_oops: %d", _compressed_oops);
st->print_cr("- compressed_class_ptrs: %d", _compressed_class_ptrs);
st->print_cr("- use_secondary_supers_table: %d", _use_secondary_supers_table);
st->print_cr("- cloned_vtables_offset: " SIZE_FORMAT_X, _cloned_vtables_offset);
st->print_cr("- serialized_data_offset: " SIZE_FORMAT_X, _serialized_data_offset);
st->print_cr("- jvm_ident: %s", _jvm_ident);
@ -2491,11 +2489,6 @@ bool FileMapHeader::validate() {
return false;
}
if (! _use_secondary_supers_table && UseSecondarySupersTable) {
log_warning(cds)("The shared archive was created without UseSecondarySupersTable.");
return false;
}
if (!_use_optimized_module_handling) {
CDSConfig::stop_using_optimized_module_handling();
log_info(cds)("optimized module handling: disabled because archive was created without optimized module handling");

View File

@ -192,7 +192,6 @@ private:
CompressedOops::Mode _narrow_oop_mode; // compressed oop encoding mode
bool _compressed_oops; // save the flag UseCompressedOops
bool _compressed_class_ptrs; // save the flag UseCompressedClassPointers
bool _use_secondary_supers_table; // save the flag UseSecondarySupersTable
size_t _cloned_vtables_offset; // The address of the first cloned vtable
size_t _serialized_data_offset; // Data accessed using {ReadClosure,WriteClosure}::serialize()
bool _has_non_jar_in_classpath; // non-jar file entry exists in classpath

View File

@ -272,7 +272,7 @@
nonstatic_field(Klass, _modifier_flags, jint) \
nonstatic_field(Klass, _access_flags, AccessFlags) \
nonstatic_field(Klass, _class_loader_data, ClassLoaderData*) \
nonstatic_field(Klass, _bitmap, uintx) \
nonstatic_field(Klass, _secondary_supers_bitmap, uintx) \
nonstatic_field(Klass, _hash_slot, uint8_t) \
nonstatic_field(Klass, _misc_flags._flags, u1) \
\

View File

@ -454,10 +454,8 @@ void Universe::genesis(TRAPS) {
_the_array_interfaces_array->at_put(1, vmClasses::Serializable_klass());
}
if (UseSecondarySupersTable) {
Universe::_the_array_interfaces_bitmap = Klass::compute_secondary_supers_bitmap(_the_array_interfaces_array);
Universe::_the_empty_klass_bitmap = Klass::compute_secondary_supers_bitmap(_the_empty_klass_array);
}
_the_array_interfaces_bitmap = Klass::compute_secondary_supers_bitmap(_the_array_interfaces_array);
_the_empty_klass_bitmap = Klass::compute_secondary_supers_bitmap(_the_empty_klass_array);
initialize_basic_type_klass(_fillerArrayKlass, CHECK);

View File

@ -51,6 +51,7 @@
#include "jvm.h"
#include "jvmtifiles/jvmti.h"
#include "logging/log.hpp"
#include "klass.inline.hpp"
#include "logging/logMessage.hpp"
#include "logging/logStream.hpp"
#include "memory/allocation.inline.hpp"
@ -652,7 +653,7 @@ void InstanceKlass::deallocate_contents(ClassLoaderData* loader_data) {
!secondary_supers()->is_shared()) {
MetadataFactory::free_array<Klass*>(loader_data, secondary_supers());
}
set_secondary_supers(nullptr);
set_secondary_supers(nullptr, SECONDARY_SUPERS_BITMAP_EMPTY);
deallocate_interfaces(loader_data, super(), local_interfaces(), transitive_interfaces());
set_transitive_interfaces(nullptr);
@ -1413,21 +1414,12 @@ GrowableArray<Klass*>* InstanceKlass::compute_secondary_supers(int num_extra_slo
// Must share this for correct bootstrapping!
set_secondary_supers(Universe::the_empty_klass_array(), Universe::the_empty_klass_bitmap());
return nullptr;
} else if (num_extra_slots == 0) {
// The secondary super list is exactly the same as the transitive interfaces, so
// let's use it instead of making a copy.
// Redefine classes has to be careful not to delete this!
if (!UseSecondarySupersTable) {
set_secondary_supers(interfaces);
return nullptr;
} else if (num_extra_slots == 0 && interfaces->length() <= 1) {
// We will reuse the transitive interfaces list if we're certain
// it's in hash order.
uintx bitmap = compute_secondary_supers_bitmap(interfaces);
set_secondary_supers(interfaces, bitmap);
return nullptr;
}
// ... fall through if that didn't work.
} else if (num_extra_slots == 0 && interfaces->length() <= 1) {
// We will reuse the transitive interfaces list if we're certain
// it's in hash order.
uintx bitmap = compute_secondary_supers_bitmap(interfaces);
set_secondary_supers(interfaces, bitmap);
return nullptr;
}
// Copy transitive interfaces to a temporary growable array to be constructed
// into the secondary super list with extra slots.
@ -3526,20 +3518,20 @@ void InstanceKlass::print_on(outputStream* st) const {
st->print(BULLET"trans. interfaces: "); transitive_interfaces()->print_value_on(st); st->cr();
st->print(BULLET"secondary supers: "); secondary_supers()->print_value_on(st); st->cr();
if (UseSecondarySupersTable) {
st->print(BULLET"hash_slot: %d", hash_slot()); st->cr();
st->print(BULLET"bitmap: " UINTX_FORMAT_X_0, _bitmap); st->cr();
}
st->print(BULLET"hash_slot: %d", hash_slot()); st->cr();
st->print(BULLET"secondary bitmap: " UINTX_FORMAT_X_0, _secondary_supers_bitmap); st->cr();
if (secondary_supers() != nullptr) {
if (Verbose) {
bool is_hashed = UseSecondarySupersTable && (_bitmap != SECONDARY_SUPERS_BITMAP_FULL);
bool is_hashed = (_secondary_supers_bitmap != SECONDARY_SUPERS_BITMAP_FULL);
st->print_cr(BULLET"---- secondary supers (%d words):", _secondary_supers->length());
for (int i = 0; i < _secondary_supers->length(); i++) {
ResourceMark rm; // for external_name()
Klass* secondary_super = _secondary_supers->at(i);
st->print(BULLET"%2d:", i);
if (is_hashed) {
int home_slot = compute_home_slot(secondary_super, _bitmap);
int home_slot = compute_home_slot(secondary_super, _secondary_supers_bitmap);
int distance = (i - home_slot) & SECONDARY_SUPERS_TABLE_MASK;
st->print(" dist:%02d:", distance);
}

View File

@ -128,7 +128,7 @@ void Klass::set_name(Symbol* n) {
_name->increment_refcount();
}
if (UseSecondarySupersTable) {
{
elapsedTimer selftime;
selftime.start();
@ -163,24 +163,51 @@ void Klass::release_C_heap_structures(bool release_constant_pool) {
if (_name != nullptr) _name->decrement_refcount();
}
bool Klass::search_secondary_supers(Klass* k) const {
// Put some extra logic here out-of-line, before the search proper.
// This cuts down the size of the inline method.
// This is necessary, since I am never in my own secondary_super list.
if (this == k)
return true;
bool Klass::linear_search_secondary_supers(const Klass* k) const {
// Scan the array-of-objects for a match
// FIXME: We could do something smarter here, maybe a vectorized
// comparison or a binary search, but is that worth any added
// complexity?
int cnt = secondary_supers()->length();
for (int i = 0; i < cnt; i++) {
if (secondary_supers()->at(i) == k) {
((Klass*)this)->set_secondary_super_cache(k);
return true;
}
}
return false;
}
// Given a secondary superklass k, an initial array index, and an
// occupancy bitmap rotated such that Bit 1 is the next bit to test,
// search for k.
bool Klass::fallback_search_secondary_supers(const Klass* k, int index, uintx rotated_bitmap) const {
// Once the occupancy bitmap is almost full, it's faster to use a
// linear search.
if (secondary_supers()->length() > SECONDARY_SUPERS_TABLE_SIZE - 2) {
return linear_search_secondary_supers(k);
}
// This is conventional linear probing, but instead of terminating
// when a null entry is found in the table, we maintain a bitmap
// in which a 0 indicates missing entries.
precond((int)population_count(rotated_bitmap) == secondary_supers()->length());
// The check for secondary_supers()->length() <= SECONDARY_SUPERS_TABLE_SIZE - 2
// at the start of this function guarantees there are 0s in the
// bitmap, so this loop eventually terminates.
while ((rotated_bitmap & 2) != 0) {
if (++index == secondary_supers()->length()) {
index = 0;
}
if (secondary_supers()->at(index) == k) {
return true;
}
rotated_bitmap = rotate_right(rotated_bitmap, 1);
}
return false;
}
// Return self, except for abstract classes with exactly 1
// implementor. Then return the 1 concrete implementation.
Klass *Klass::up_cast_abstract() {
@ -256,7 +283,7 @@ Klass::Klass() : _kind(UnknownKlassKind) {
// The constructor is also used from CppVtableCloner,
// which doesn't zero out the memory before calling the constructor.
Klass::Klass(KlassKind kind) : _kind(kind),
_shared_class_path_index(-1) {
_shared_class_path_index(-1) {
CDS_ONLY(_shared_class_flags = 0;)
CDS_JAVA_HEAP_ONLY(_archived_mirror_index = -1;)
_primary_supers[0] = this;
@ -292,20 +319,15 @@ bool Klass::can_be_primary_super_slow() const {
return true;
}
void Klass::set_secondary_supers(Array<Klass*>* secondaries) {
assert(!UseSecondarySupersTable || secondaries == nullptr, "");
set_secondary_supers(secondaries, SECONDARY_SUPERS_BITMAP_EMPTY);
}
void Klass::set_secondary_supers(Array<Klass*>* secondaries, uintx bitmap) {
#ifdef ASSERT
if (UseSecondarySupersTable && secondaries != nullptr) {
if (secondaries != nullptr) {
uintx real_bitmap = compute_secondary_supers_bitmap(secondaries);
assert(bitmap == real_bitmap, "must be");
assert(secondaries->length() >= (int)population_count(bitmap), "must be");
}
#endif
_bitmap = bitmap;
_secondary_supers_bitmap = bitmap;
_secondary_supers = secondaries;
if (secondaries != nullptr) {
@ -382,6 +404,7 @@ uintx Klass::hash_secondary_supers(Array<Klass*>* secondaries, bool rewrite) {
}
}
assert(i == secondaries->length(), "mismatch");
postcond((int)population_count(bitmap) == secondaries->length());
return bitmap;
}
@ -442,11 +465,7 @@ Array<Klass*>* Klass::pack_secondary_supers(ClassLoaderData* loader_data,
}
#endif
if (UseSecondarySupersTable) {
bitmap = hash_secondary_supers(secondary_supers, /*rewrite=*/true); // rewrites freshly allocated array
} else {
bitmap = SECONDARY_SUPERS_BITMAP_EMPTY;
}
bitmap = hash_secondary_supers(secondary_supers, /*rewrite=*/true); // rewrites freshly allocated array
return secondary_supers;
}
@ -770,7 +789,7 @@ void Klass::remove_unshareable_info() {
// FIXME: validation in Klass::hash_secondary_supers() may fail for shared klasses.
// Even though the bitmaps always match, the canonical order of elements in the table
// is not guaranteed to stay the same (see tie breaker during Robin Hood hashing in Klass::hash_insert).
//assert(compute_secondary_supers_bitmap(secondary_supers()) == _bitmap, "broken table");
//assert(compute_secondary_supers_bitmap(secondary_supers()) == _secondary_supers_bitmap, "broken table");
}
void Klass::remove_java_mirror() {
@ -786,7 +805,7 @@ void Klass::remove_java_mirror() {
void Klass::restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS) {
assert(is_klass(), "ensure C++ vtable is restored");
assert(is_shared(), "must be set");
assert(secondary_supers()->length() >= (int)population_count(_bitmap), "must be");
assert(secondary_supers()->length() >= (int)population_count(_secondary_supers_bitmap), "must be");
JFR_ONLY(RESTORE_ID(this);)
if (log_is_enabled(Trace, cds, unshareable)) {
ResourceMark rm(THREAD);
@ -1253,14 +1272,13 @@ static void print_negative_lookup_stats(uintx bitmap, outputStream* st) {
void Klass::print_secondary_supers_on(outputStream* st) const {
if (secondary_supers() != nullptr) {
if (UseSecondarySupersTable) {
st->print(" - "); st->print("%d elements;", _secondary_supers->length());
st->print_cr(" bitmap: " UINTX_FORMAT_X_0 ";", _bitmap);
if (_bitmap != SECONDARY_SUPERS_BITMAP_EMPTY &&
_bitmap != SECONDARY_SUPERS_BITMAP_FULL) {
st->print(" - "); print_positive_lookup_stats(secondary_supers(), _bitmap, st); st->cr();
st->print(" - "); print_negative_lookup_stats(_bitmap, st); st->cr();
}
st->print(" - "); st->print("%d elements;", _secondary_supers->length());
st->print_cr(" bitmap: " UINTX_FORMAT_X_0 ";", _secondary_supers_bitmap);
if (_secondary_supers_bitmap != SECONDARY_SUPERS_BITMAP_EMPTY &&
_secondary_supers_bitmap != SECONDARY_SUPERS_BITMAP_FULL) {
st->print(" - "); print_positive_lookup_stats(secondary_supers(),
_secondary_supers_bitmap, st); st->cr();
st->print(" - "); print_negative_lookup_stats(_secondary_supers_bitmap, st); st->cr();
}
} else {
st->print("null");
@ -1271,7 +1289,6 @@ void Klass::on_secondary_supers_verification_failure(Klass* super, Klass* sub, b
ResourceMark rm;
super->print();
sub->print();
fatal("%s: %s implements %s: is_subtype_of: %d; linear_search: %d; table_lookup: %d",
msg, sub->external_name(), super->external_name(),
sub->is_subtype_of(super), linear_result, table_result);
fatal("%s: %s implements %s: linear_search: %d; table_lookup: %d",
msg, sub->external_name(), super->external_name(), linear_result, table_result);
}

View File

@ -65,6 +65,7 @@ class PSPromotionManager;
class vtableEntry;
class Klass : public Metadata {
friend class VMStructs;
friend class JVMCIVMStructs;
public:
@ -160,6 +161,9 @@ class Klass : public Metadata {
// Provide access the corresponding instance java.lang.ClassLoader.
ClassLoaderData* _class_loader_data;
// Bitmap and hash code used by hashed secondary supers.
uintx _secondary_supers_bitmap;
uint8_t _hash_slot;
int _vtable_len; // vtable length. This field may be read very often when we
// have lots of itable dispatches (e.g., lambdas and streams).
@ -171,10 +175,6 @@ class Klass : public Metadata {
JFR_ONLY(DEFINE_TRACE_ID_FIELD;)
// Bitmap and hash code used by hashed secondary supers.
uintx _bitmap;
uint8_t _hash_slot;
private:
// This is an index into FileMapHeader::_shared_path_table[], to
// associate this class with the JAR file where it's loaded from during
@ -239,7 +239,6 @@ protected:
void set_secondary_super_cache(Klass* k) { _secondary_super_cache = k; }
Array<Klass*>* secondary_supers() const { return _secondary_supers; }
void set_secondary_supers(Array<Klass*>* k);
void set_secondary_supers(Array<Klass*>* k, uintx bitmap);
uint8_t hash_slot() const { return _hash_slot; }
@ -398,6 +397,11 @@ protected:
static void hash_insert(Klass* klass, GrowableArray<Klass*>* secondaries, uintx& bitmap);
static uintx hash_secondary_supers(Array<Klass*>* secondaries, bool rewrite);
bool search_secondary_supers(Klass* k) const;
bool lookup_secondary_supers_table(Klass *k) const;
bool linear_search_secondary_supers(const Klass* k) const;
bool fallback_search_secondary_supers(const Klass* k, int index, uintx rotated_bitmap) const;
public:
// Secondary supers table support
static Array<Klass*>* pack_secondary_supers(ClassLoaderData* loader_data,
@ -409,7 +413,7 @@ protected:
static uintx compute_secondary_supers_bitmap(Array<Klass*>* secondary_supers);
static uint8_t compute_home_slot(Klass* k, uintx bitmap);
static constexpr int SECONDARY_SUPERS_TABLE_SIZE = sizeof(_bitmap) * 8;
static constexpr int SECONDARY_SUPERS_TABLE_SIZE = sizeof(_secondary_supers_bitmap) * 8;
static constexpr int SECONDARY_SUPERS_TABLE_MASK = SECONDARY_SUPERS_TABLE_SIZE - 1;
static constexpr uintx SECONDARY_SUPERS_BITMAP_EMPTY = 0;
@ -430,7 +434,9 @@ protected:
static ByteSize subklass_offset() { return byte_offset_of(Klass, _subklass); }
static ByteSize next_sibling_offset() { return byte_offset_of(Klass, _next_sibling); }
#endif
static ByteSize bitmap_offset() { return byte_offset_of(Klass, _bitmap); }
static ByteSize secondary_supers_bitmap_offset()
{ return byte_offset_of(Klass, _secondary_supers_bitmap); }
static ByteSize hash_slot_offset() { return byte_offset_of(Klass, _hash_slot); }
static ByteSize misc_flags_offset() { return byte_offset_of(Klass, _misc_flags._flags); }
// Unpacking layout_helper:
@ -531,22 +537,11 @@ protected:
// subclass check
bool is_subclass_of(const Klass* k) const;
// subtype check: true if is_subclass_of, or if k is interface and receiver implements it
bool is_subtype_of(Klass* k) const {
juint off = k->super_check_offset();
Klass* sup = *(Klass**)( (address)this + off );
const juint secondary_offset = in_bytes(secondary_super_cache_offset());
if (sup == k) {
return true;
} else if (off != secondary_offset) {
return false;
} else {
return search_secondary_supers(k);
}
}
bool search_secondary_supers(Klass* k) const;
bool is_subtype_of(Klass* k) const;
public:
// Find LCA in class hierarchy
Klass *LCA( Klass *k );

View File

@ -30,6 +30,7 @@
#include "classfile/classLoaderData.inline.hpp"
#include "oops/klassVtable.hpp"
#include "oops/markWord.hpp"
#include "utilities/rotate_bits.hpp"
// This loads and keeps the klass's loader alive.
inline oop Klass::klass_holder() const {
@ -75,4 +76,72 @@ inline ByteSize Klass::vtable_start_offset() {
return in_ByteSize(InstanceKlass::header_size() * wordSize);
}
// subtype check: true if is_subclass_of, or if k is interface and receiver implements it
inline bool Klass::is_subtype_of(Klass* k) const {
assert(secondary_supers() != nullptr, "must be");
const juint off = k->super_check_offset();
const juint secondary_offset = in_bytes(secondary_super_cache_offset());
if (off == secondary_offset) {
return search_secondary_supers(k);
} else {
Klass* sup = *(Klass**)( (address)this + off );
return (sup == k);
}
}
// Hashed search for secondary super k.
inline bool Klass::lookup_secondary_supers_table(Klass* k) const {
uintx bitmap = _secondary_supers_bitmap;
constexpr int highest_bit_number = SECONDARY_SUPERS_TABLE_SIZE - 1;
uint8_t slot = k->_hash_slot;
uintx shifted_bitmap = bitmap << (highest_bit_number - slot);
precond((int)population_count(bitmap) <= secondary_supers()->length());
// First check the bitmap to see if super_klass might be present. If
// the bit is zero, we are certain that super_klass is not one of
// the secondary supers.
if (((shifted_bitmap >> highest_bit_number) & 1) == 0) {
return false;
}
// Calculate the initial hash probe
int index = population_count(shifted_bitmap) - 1;
if (secondary_supers()->at(index) == k) {
// Yes! It worked the first time.
return true;
}
// Is there another entry to check? Consult the bitmap. If Bit 1,
// the next bit to test, is zero, we are certain that super_klass is
// not one of the secondary supers.
bitmap = rotate_right(bitmap, slot);
if ((bitmap & 2) == 0) {
return false;
}
// Continue probing the hash table
return fallback_search_secondary_supers(k, index, bitmap);
}
inline bool Klass::search_secondary_supers(Klass *k) const {
// This is necessary because I am never in my own secondary_super list.
if (this == k)
return true;
bool result = lookup_secondary_supers_table(k);
#ifndef PRODUCT
if (VerifySecondarySupers) {
bool linear_result = linear_search_secondary_supers(k);
if (linear_result != result) {
on_secondary_supers_verification_failure((Klass*)this, k, linear_result, result, "mismatch");
}
}
#endif // PRODUCT
return result;
}
#endif // SHARE_OOPS_KLASS_INLINE_HPP