Mercurial > hg > graal-compiler
diff src/cpu/sparc/vm/assembler_sparc.cpp @ 644:c517646eef23
6813212: factor duplicated assembly code for general subclass check (for 6655638)
Summary: Code in interp_masm, stubGenerator, c1_LIRAssembler, and AD files moved into MacroAssembler.
Reviewed-by: kvn
author | jrose |
---|---|
date | Fri, 13 Mar 2009 18:39:22 -0700 |
parents | 660978a2a31a |
children | c89f86385056 96b229c54d1e |
line wrap: on
line diff
--- a/src/cpu/sparc/vm/assembler_sparc.cpp Fri Mar 13 11:35:17 2009 -0700 +++ b/src/cpu/sparc/vm/assembler_sparc.cpp Fri Mar 13 18:39:22 2009 -0700 @@ -2767,6 +2767,268 @@ } +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,