diff --git a/src/cpu/ppc/vm/globals_ppc.hpp b/src/cpu/ppc/vm/globals_ppc.hpp index b39151004c9f014a6fca64765b6d8dae76e2f776..1e3c327765fa58e63504febd8a0f6c97317233a0 100644 --- a/src/cpu/ppc/vm/globals_ppc.hpp +++ b/src/cpu/ppc/vm/globals_ppc.hpp @@ -38,6 +38,7 @@ define_pd_global(bool, NeedsDeoptSuspend, false); // Only register window ma define_pd_global(bool, ImplicitNullChecks, true); // Generate code for implicit null checks. +define_pd_global(bool, TrapBasedNullChecks, true); define_pd_global(bool, UncommonNullCast, true); // Uncommon-trap NULLs passed to check cast. // Use large code-entry alignment. @@ -100,12 +101,6 @@ define_pd_global(uintx, TypeProfileLevel, 0); product(bool, TrapBasedNotEntrantChecks, true, \ "Raise and handle SIGTRAP if calling not entrant or zombie" \ " method.") \ - product(bool, TrapBasedNullChecks, true, \ - "Generate code for null checks that uses a cmp and trap " \ - "instruction raising SIGTRAP. This is only used if an access to" \ - "null (+offset) will not raise a SIGSEGV.") \ - product(bool, TrapBasedRangeChecks, true, \ - "Raise and handle SIGTRAP if array out of bounds check fails.") \ product(bool, TraceTraps, false, "Trace all traps the signal handler" \ "handles.") \ \ diff --git a/src/cpu/ppc/vm/nativeInst_ppc.hpp b/src/cpu/ppc/vm/nativeInst_ppc.hpp index b70a859bf301e559e8774b27d9751478cadfa418..c2ff51183c153d9710ea48182483976543140ecc 100644 --- a/src/cpu/ppc/vm/nativeInst_ppc.hpp +++ b/src/cpu/ppc/vm/nativeInst_ppc.hpp @@ -76,11 +76,13 @@ class NativeInstruction VALUE_OBJ_CLASS_SPEC { } static bool is_sigill_zombie_not_entrant_at(address addr); +#ifdef COMPILER2 // SIGTRAP-based implicit range checks bool is_sigtrap_range_check() { assert(UseSIGTRAP && TrapBasedRangeChecks, "precondition"); return MacroAssembler::is_trap_range_check(long_at(0)); } +#endif // 'should not reach here'. bool is_sigtrap_should_not_reach_here() { diff --git a/src/cpu/ppc/vm/vm_version_ppc.cpp b/src/cpu/ppc/vm/vm_version_ppc.cpp index 079b781d65551af6f57df2eee4364b43c0c6c414..de2af7c34b6f77a8c27a3957658a92133bf041d5 100644 --- a/src/cpu/ppc/vm/vm_version_ppc.cpp +++ b/src/cpu/ppc/vm/vm_version_ppc.cpp @@ -77,14 +77,17 @@ void VM_Version::initialize() { MSG(TrapBasedICMissChecks); MSG(TrapBasedNotEntrantChecks); MSG(TrapBasedNullChecks); - MSG(TrapBasedRangeChecks); FLAG_SET_ERGO(bool, TrapBasedNotEntrantChecks, false); FLAG_SET_ERGO(bool, TrapBasedNullChecks, false); FLAG_SET_ERGO(bool, TrapBasedICMissChecks, false); - FLAG_SET_ERGO(bool, TrapBasedRangeChecks, false); } #ifdef COMPILER2 + if (!UseSIGTRAP) { + MSG(TrapBasedRangeChecks); + FLAG_SET_ERGO(bool, TrapBasedRangeChecks, false); + } + // On Power6 test for section size. if (PowerArchitecturePPC64 == 6) determine_section_size(); diff --git a/src/cpu/sparc/vm/c2_globals_sparc.hpp b/src/cpu/sparc/vm/c2_globals_sparc.hpp index e4fe6bb00a4f1808c20647e48d05acf85f4a4a19..32747c798c4f046becf0b000629675c9ef09af04 100644 --- a/src/cpu/sparc/vm/c2_globals_sparc.hpp +++ b/src/cpu/sparc/vm/c2_globals_sparc.hpp @@ -90,6 +90,8 @@ define_pd_global(uint64_t,MaxRAM, 4ULL*G); define_pd_global(uintx, CodeCacheMinBlockLength, 4); define_pd_global(uintx, CodeCacheMinimumUseSpace, 400*K); +define_pd_global(bool, TrapBasedRangeChecks, false); // Not needed on sparc. + // Heap related flags define_pd_global(uintx,MetaspaceSize, ScaleForWordSize(16*M)); diff --git a/src/cpu/sparc/vm/globals_sparc.hpp b/src/cpu/sparc/vm/globals_sparc.hpp index ecc6eb61524b09a9582c559240e0281333dbe282..8dd047927a1b1d57ddea632cb004477179948fac 100644 --- a/src/cpu/sparc/vm/globals_sparc.hpp +++ b/src/cpu/sparc/vm/globals_sparc.hpp @@ -43,7 +43,8 @@ define_pd_global(bool, CountInterpCalls, false); // not implemented i define_pd_global(bool, NeedsDeoptSuspend, true); // register window machines need this define_pd_global(bool, ImplicitNullChecks, true); // Generate code for implicit null checks -define_pd_global(bool, UncommonNullCast, true); // Uncommon-trap NULLs past to check cast +define_pd_global(bool, TrapBasedNullChecks, false); // Not needed on sparc. +define_pd_global(bool, UncommonNullCast, true); // Uncommon-trap NULLs passed to check cast define_pd_global(intx, CodeEntryAlignment, 32); // The default setting 16/16 seems to work best. diff --git a/src/cpu/x86/vm/c2_globals_x86.hpp b/src/cpu/x86/vm/c2_globals_x86.hpp index a45bd9624e5a2ba019cf7bfcff3bb3b52238bc96..6ca78039c8b5c9949a11c833e6ff24d12bddd0a7 100644 --- a/src/cpu/x86/vm/c2_globals_x86.hpp +++ b/src/cpu/x86/vm/c2_globals_x86.hpp @@ -88,6 +88,8 @@ define_pd_global(intx, ReservedCodeCacheSize, 48*M); define_pd_global(uintx, CodeCacheMinBlockLength, 4); define_pd_global(uintx, CodeCacheMinimumUseSpace, 400*K); +define_pd_global(bool, TrapBasedRangeChecks, false); // Not needed on x86. + // Heap related flags define_pd_global(uintx,MetaspaceSize, ScaleForWordSize(16*M)); diff --git a/src/cpu/x86/vm/globals_x86.hpp b/src/cpu/x86/vm/globals_x86.hpp index b194ffbcfbe4ee1952c0deaec5b5a5ce2cb93c11..e82c192901bcab6526ec2b7c5c31d56977f60f61 100644 --- a/src/cpu/x86/vm/globals_x86.hpp +++ b/src/cpu/x86/vm/globals_x86.hpp @@ -37,7 +37,8 @@ define_pd_global(bool, CountInterpCalls, true); define_pd_global(bool, NeedsDeoptSuspend, false); // only register window machines need this define_pd_global(bool, ImplicitNullChecks, true); // Generate code for implicit null checks -define_pd_global(bool, UncommonNullCast, true); // Uncommon-trap NULLs past to check cast +define_pd_global(bool, TrapBasedNullChecks, false); // Not needed on x86. +define_pd_global(bool, UncommonNullCast, true); // Uncommon-trap NULLs passed to check cast // See 4827828 for this change. There is no globals_core_i486.hpp. I can't // assign a different value for C2 without touching a number of files. Use diff --git a/src/cpu/zero/vm/globals_zero.hpp b/src/cpu/zero/vm/globals_zero.hpp index 67fcfba5720c3f151aeb9293b8be2597aa78cb65..993064536bba94fca616f047e60db81eb33b51e5 100644 --- a/src/cpu/zero/vm/globals_zero.hpp +++ b/src/cpu/zero/vm/globals_zero.hpp @@ -38,6 +38,7 @@ define_pd_global(bool, CountInterpCalls, true); define_pd_global(bool, NeedsDeoptSuspend, false); define_pd_global(bool, ImplicitNullChecks, true); +define_pd_global(bool, TrapBasedNullChecks, false); define_pd_global(bool, UncommonNullCast, true); define_pd_global(intx, CodeEntryAlignment, 32); diff --git a/src/share/vm/adlc/main.cpp b/src/share/vm/adlc/main.cpp index 2b61bdbeb736c7a6cc4ba9d6398f2fb670e91931..612d0e582d945c912c3c245145ee439da0255713 100644 --- a/src/share/vm/adlc/main.cpp +++ b/src/share/vm/adlc/main.cpp @@ -272,6 +272,7 @@ int main(int argc, char *argv[]) AD.addInclude(AD._CPP_PIPELINE_file, "adfiles", get_basename(AD._HPP_file._name)); AD.addInclude(AD._DFA_file, "precompiled.hpp"); AD.addInclude(AD._DFA_file, "adfiles", get_basename(AD._HPP_file._name)); + AD.addInclude(AD._DFA_file, "opto/cfgnode.hpp"); // Use PROB_MAX in predicate. AD.addInclude(AD._DFA_file, "opto/matcher.hpp"); AD.addInclude(AD._DFA_file, "opto/opcodes.hpp"); // Make sure each .cpp file starts with include lines: diff --git a/src/share/vm/adlc/output_h.cpp b/src/share/vm/adlc/output_h.cpp index 34a3ba9681a29d5f071391d4407a15b678d54728..fa9b79a172af29f7e95fd227261f3bc2ddd2e656 100644 --- a/src/share/vm/adlc/output_h.cpp +++ b/src/share/vm/adlc/output_h.cpp @@ -1615,15 +1615,19 @@ void ArchDesc::declareClasses(FILE *fp) { Attribute *attr = instr->_attribs; bool avoid_back_to_back = false; while (attr != NULL) { - if (strcmp (attr->_ident,"ins_cost") && - strncmp(attr->_ident,"ins_field_", 10) != 0 && - strcmp (attr->_ident,"ins_short_branch")) { - fprintf(fp," virtual int %s() const { return %s; }\n", - attr->_ident, attr->_val); + if (strcmp (attr->_ident, "ins_cost") != 0 && + strncmp(attr->_ident, "ins_field_", 10) != 0 && + // Must match function in node.hpp: return type bool, no prefix "ins_". + strcmp (attr->_ident, "ins_is_TrapBasedCheckNode") != 0 && + strcmp (attr->_ident, "ins_short_branch") != 0) { + fprintf(fp, " virtual int %s() const { return %s; }\n", attr->_ident, attr->_val); } // Check value for ins_avoid_back_to_back, and if it is true (1), set the flag - if (!strcmp(attr->_ident,"ins_avoid_back_to_back") && attr->int_val(*this) != 0) + if (!strcmp(attr->_ident, "ins_avoid_back_to_back") != 0 && attr->int_val(*this) != 0) avoid_back_to_back = true; + if (strcmp (attr->_ident, "ins_is_TrapBasedCheckNode") == 0) + fprintf(fp, " virtual bool is_TrapBasedCheckNode() const { return %s; }\n", attr->_val); + attr = (Attribute *)attr->_next; } diff --git a/src/share/vm/opto/block.cpp b/src/share/vm/opto/block.cpp index 4b7fc6409cea59f5c7978a6345512ae4a7fd52e5..9d2f9522bed9cb530ec42445ea9ca5172f4780b4 100644 --- a/src/share/vm/opto/block.cpp +++ b/src/share/vm/opto/block.cpp @@ -530,18 +530,27 @@ void PhaseCFG::insert_goto_at(uint block_no, uint succ_no) { // Does this block end in a multiway branch that cannot have the default case // flipped for another case? -static bool no_flip_branch( Block *b ) { +static bool no_flip_branch(Block *b) { int branch_idx = b->number_of_nodes() - b->_num_succs-1; - if( branch_idx < 1 ) return false; - Node *bra = b->get_node(branch_idx); - if( bra->is_Catch() ) + if (branch_idx < 1) { + return false; + } + Node *branch = b->get_node(branch_idx); + if (branch->is_Catch()) { return true; - if( bra->is_Mach() ) { - if( bra->is_MachNullCheck() ) + } + if (branch->is_Mach()) { + if (branch->is_MachNullCheck()) { + return true; + } + int iop = branch->as_Mach()->ideal_Opcode(); + if (iop == Op_FastLock || iop == Op_FastUnlock) { return true; - int iop = bra->as_Mach()->ideal_Opcode(); - if( iop == Op_FastLock || iop == Op_FastUnlock ) + } + // Don't flip if branch has an implicit check. + if (branch->as_Mach()->is_TrapBasedCheckNode()) { return true; + } } return false; } @@ -700,6 +709,57 @@ void PhaseCFG::remove_empty_blocks() { } // End of for all blocks } +Block *PhaseCFG::fixup_trap_based_check(Node *branch, Block *block, int block_pos, Block *bnext) { + // Trap based checks must fall through to the successor with + // PROB_ALWAYS. + // They should be an If with 2 successors. + assert(branch->is_MachIf(), "must be If"); + assert(block->_num_succs == 2, "must have 2 successors"); + + // Get the If node and the projection for the first successor. + MachIfNode *iff = block->get_node(block->number_of_nodes()-3)->as_MachIf(); + ProjNode *proj0 = block->get_node(block->number_of_nodes()-2)->as_Proj(); + ProjNode *proj1 = block->get_node(block->number_of_nodes()-1)->as_Proj(); + ProjNode *projt = (proj0->Opcode() == Op_IfTrue) ? proj0 : proj1; + ProjNode *projf = (proj0->Opcode() == Op_IfFalse) ? proj0 : proj1; + + // Assert that proj0 and succs[0] match up. Similarly for proj1 and succs[1]. + assert(proj0->raw_out(0) == block->_succs[0]->head(), "Mismatch successor 0"); + assert(proj1->raw_out(0) == block->_succs[1]->head(), "Mismatch successor 1"); + + ProjNode *proj_always; + ProjNode *proj_never; + // We must negate the branch if the implicit check doesn't follow + // the branch's TRUE path. Then, the new TRUE branch target will + // be the old FALSE branch target. + if (iff->_prob <= 2*PROB_NEVER) { // There are small rounding errors. + proj_never = projt; + proj_always = projf; + } else { + // We must negate the branch if the trap doesn't follow the + // branch's TRUE path. Then, the new TRUE branch target will + // be the old FALSE branch target. + proj_never = projf; + proj_always = projt; + iff->negate(); + } + assert(iff->_prob <= 2*PROB_NEVER, "Trap based checks are expected to trap never!"); + // Map the successors properly + block->_succs.map(0, get_block_for_node(proj_never ->raw_out(0))); // The target of the trap. + block->_succs.map(1, get_block_for_node(proj_always->raw_out(0))); // The fall through target. + + // Place the fall through block after this block. + Block *bs1 = block->non_connector_successor(1); + if (bs1 != bnext && move_to_next(bs1, block_pos)) { + bnext = bs1; + } + // If the fall through block still is not the next block, insert a goto. + if (bs1 != bnext) { + insert_goto_at(block_pos, 1); + } + return bnext; +} + // Fix up the final control flow for basic blocks. void PhaseCFG::fixup_flow() { // Fixup final control flow for the blocks. Remove jump-to-next @@ -723,25 +783,39 @@ void PhaseCFG::fixup_flow() { // Check for multi-way branches where I cannot negate the test to // exchange the true and false targets. if (no_flip_branch(block)) { - // Find fall through case - if must fall into its target + // Find fall through case - if must fall into its target. + // Get the index of the branch's first successor. int branch_idx = block->number_of_nodes() - block->_num_succs; - for (uint j2 = 0; j2 < block->_num_succs; j2++) { - const ProjNode* p = block->get_node(branch_idx + j2)->as_Proj(); - if (p->_con == 0) { - // successor j2 is fall through case - if (block->non_connector_successor(j2) != bnext) { - // but it is not the next block => insert a goto - insert_goto_at(i, j2); - } - // Put taken branch in slot 0 - if (j2 == 0 && block->_num_succs == 2) { - // Flip targets in succs map - Block *tbs0 = block->_succs[0]; - Block *tbs1 = block->_succs[1]; - block->_succs.map(0, tbs1); - block->_succs.map(1, tbs0); + + // The branch is 1 before the branch's first successor. + Node *branch = block->get_node(branch_idx-1); + + // Handle no-flip branches which have implicit checks and which require + // special block ordering and individual semantics of the 'fall through + // case'. + if ((TrapBasedNullChecks || TrapBasedRangeChecks) && + branch->is_Mach() && branch->as_Mach()->is_TrapBasedCheckNode()) { + bnext = fixup_trap_based_check(branch, block, i, bnext); + } else { + // Else, default handling for no-flip branches + for (uint j2 = 0; j2 < block->_num_succs; j2++) { + const ProjNode* p = block->get_node(branch_idx + j2)->as_Proj(); + if (p->_con == 0) { + // successor j2 is fall through case + if (block->non_connector_successor(j2) != bnext) { + // but it is not the next block => insert a goto + insert_goto_at(i, j2); + } + // Put taken branch in slot 0 + if (j2 == 0 && block->_num_succs == 2) { + // Flip targets in succs map + Block *tbs0 = block->_succs[0]; + Block *tbs1 = block->_succs[1]; + block->_succs.map(0, tbs1); + block->_succs.map(1, tbs0); + } + break; } - break; } } diff --git a/src/share/vm/opto/block.hpp b/src/share/vm/opto/block.hpp index 1d6028bd5ea420a9a30a4c1d5df22dde7c5d13e6..76393a821006d54e9e924b687f7ac7ce661a8be6 100644 --- a/src/share/vm/opto/block.hpp +++ b/src/share/vm/opto/block.hpp @@ -590,6 +590,7 @@ class PhaseCFG : public Phase { // Remove empty basic blocks void remove_empty_blocks(); + Block *fixup_trap_based_check(Node *branch, Block *block, int block_pos, Block *bnext); void fixup_flow(); // Insert a node into a block at index and map the node to the block diff --git a/src/share/vm/opto/c2_globals.hpp b/src/share/vm/opto/c2_globals.hpp index 98805be9ced7d7b15b0fcd979d722103b56d3b98..9ae827b663100d0e9213cb5424ffc822011adef5 100644 --- a/src/share/vm/opto/c2_globals.hpp +++ b/src/share/vm/opto/c2_globals.hpp @@ -654,7 +654,11 @@ "Propagate type improvements in callers of inlinee if possible") \ \ experimental(bool, UseTypeSpeculation, false, \ - "Speculatively propagate types from profiles") + "Speculatively propagate types from profiles") \ + \ + product_pd(bool, TrapBasedRangeChecks, \ + "Generate code for range checks that uses a cmp and trap " \ + "instruction raising SIGTRAP. Used on PPC64.") \ C2_FLAGS(DECLARE_DEVELOPER_FLAG, DECLARE_PD_DEVELOPER_FLAG, DECLARE_PRODUCT_FLAG, DECLARE_PD_PRODUCT_FLAG, DECLARE_DIAGNOSTIC_FLAG, DECLARE_EXPERIMENTAL_FLAG, DECLARE_NOTPRODUCT_FLAG) diff --git a/src/share/vm/opto/compile.cpp b/src/share/vm/opto/compile.cpp index 069e8d7646b71b9dd25dda995595b4927a772b87..8b3c21555b8e187fcd48a21ee07b7bbf617dabd5 100644 --- a/src/share/vm/opto/compile.cpp +++ b/src/share/vm/opto/compile.cpp @@ -860,6 +860,10 @@ Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr int next_slot = _orig_pc_slot + (sizeof(address) / VMRegImpl::stack_slot_size); set_fixed_slots(next_slot); + // Compute when to use implicit null checks. Used by matching trap based + // nodes and NullCheck optimization. + set_allowed_deopt_reasons(); + // Now generate code Code_Gen(); if (failing()) return; @@ -948,7 +952,8 @@ Compile::Compile( ciEnv* ci_env, _inlining_incrementally(false), _print_inlining_list(NULL), _print_inlining_idx(0), - _preserve_jvm_state(0) { + _preserve_jvm_state(0), + _allowed_reasons(0) { C = this; #ifndef PRODUCT @@ -3350,6 +3355,19 @@ bool Compile::too_many_recompiles(ciMethod* method, } } +// Compute when not to trap. Used by matching trap based nodes and +// NullCheck optimization. +void Compile::set_allowed_deopt_reasons() { + _allowed_reasons = 0; + if (is_method_compilation()) { + for (int rs = (int)Deoptimization::Reason_none+1; rs < Compile::trapHistLength; rs++) { + assert(rs < BitsPerInt, "recode bit map"); + if (!too_many_traps((Deoptimization::DeoptReason) rs)) { + _allowed_reasons |= nth_bit(rs); + } + } + } +} #ifndef PRODUCT //------------------------------verify_graph_edges--------------------------- diff --git a/src/share/vm/opto/compile.hpp b/src/share/vm/opto/compile.hpp index 96b847179d5cbd3696041749fc6b5295947fff84..acc2f1c6cad26ecbbeb21d673c4b28971c6c36e5 100644 --- a/src/share/vm/opto/compile.hpp +++ b/src/share/vm/opto/compile.hpp @@ -855,6 +855,11 @@ class Compile : public Phase { ciMethodData* logmd = NULL); // Report if there were too many recompiles at a method and bci. bool too_many_recompiles(ciMethod* method, int bci, Deoptimization::DeoptReason reason); + // Return a bitset with the reasons where deoptimization is allowed, + // i.e., where there were not too many uncommon traps. + int _allowed_reasons; + int allowed_deopt_reasons() { return _allowed_reasons; } + void set_allowed_deopt_reasons(); // Parsing, optimization PhaseGVN* initial_gvn() { return _initial_gvn; } diff --git a/src/share/vm/opto/gcm.cpp b/src/share/vm/opto/gcm.cpp index 33720499471646f4c74294c64c5ce056a462fe27..58dac3c42deec7719f9071d5142325dff24fa27a 100644 --- a/src/share/vm/opto/gcm.cpp +++ b/src/share/vm/opto/gcm.cpp @@ -1330,15 +1330,6 @@ void PhaseCFG::global_code_motion() { // with suitable memory ops nearby. Use the memory op to do the NULL check. // I can generate a memory op if there is not one nearby. if (C->is_method_compilation()) { - // Don't do it for natives, adapters, or runtime stubs - int allowed_reasons = 0; - // ...and don't do it when there have been too many traps, globally. - for (int reason = (int)Deoptimization::Reason_none+1; - reason < Compile::trapHistLength; reason++) { - assert(reason < BitsPerInt, "recode bit map"); - if (!C->too_many_traps((Deoptimization::DeoptReason) reason)) - allowed_reasons |= nth_bit(reason); - } // By reversing the loop direction we get a very minor gain on mpegaudio. // Feel free to revert to a forward loop for clarity. // for( int i=0; i < (int)matcher._null_check_tests.size(); i+=2 ) { @@ -1346,7 +1337,7 @@ void PhaseCFG::global_code_motion() { Node* proj = _matcher._null_check_tests[i]; Node* val = _matcher._null_check_tests[i + 1]; Block* block = get_block_for_node(proj); - implicit_null_check(block, proj, val, allowed_reasons); + implicit_null_check(block, proj, val, C->allowed_deopt_reasons()); // The implicit_null_check will only perform the transformation // if the null branch is truly uncommon, *and* it leads to an // uncommon trap. Combined with the too_many_traps guards diff --git a/src/share/vm/opto/machnode.hpp b/src/share/vm/opto/machnode.hpp index 213087247ff66c0ba50cc20ca7aa25966dc64f46..35498dbd5f50e6972f8d396cb6c7abe29b2ed203 100644 --- a/src/share/vm/opto/machnode.hpp +++ b/src/share/vm/opto/machnode.hpp @@ -315,6 +315,9 @@ public: static const Pipeline *pipeline_class(); virtual const Pipeline *pipeline() const; + // Returns true if this node is a check that can be implemented with a trap. + virtual bool is_TrapBasedCheckNode() const { return false; } + #ifndef PRODUCT virtual const char *Name() const = 0; // Machine-specific name virtual void dump_spec(outputStream *st) const; // Print per-node info diff --git a/src/share/vm/opto/matcher.cpp b/src/share/vm/opto/matcher.cpp index 708711e2a64b2436513c2562e48bcac01fdc7ff7..311bd24933552c677b9d9e50258f083e13a58bbc 100644 --- a/src/share/vm/opto/matcher.cpp +++ b/src/share/vm/opto/matcher.cpp @@ -2395,6 +2395,69 @@ bool Matcher::post_store_load_barrier(const Node* vmb) { return false; } +// Check whether node n is a branch to an uncommon trap that we could +// optimize as test with very high branch costs in case of going to +// the uncommon trap. The code must be able to be recompiled to use +// a cheaper test. +bool Matcher::branches_to_uncommon_trap(const Node *n) { + // Don't do it for natives, adapters, or runtime stubs + Compile *C = Compile::current(); + if (!C->is_method_compilation()) return false; + + assert(n->is_If(), "You should only call this on if nodes."); + IfNode *ifn = n->as_If(); + + Node *ifFalse = NULL; + for (DUIterator_Fast imax, i = ifn->fast_outs(imax); i < imax; i++) { + if (ifn->fast_out(i)->is_IfFalse()) { + ifFalse = ifn->fast_out(i); + break; + } + } + assert(ifFalse, "An If should have an ifFalse. Graph is broken."); + + Node *reg = ifFalse; + int cnt = 4; // We must protect against cycles. Limit to 4 iterations. + // Alternatively use visited set? Seems too expensive. + while (reg != NULL && cnt > 0) { + CallNode *call = NULL; + RegionNode *nxt_reg = NULL; + for (DUIterator_Fast imax, i = reg->fast_outs(imax); i < imax; i++) { + Node *o = reg->fast_out(i); + if (o->is_Call()) { + call = o->as_Call(); + } + if (o->is_Region()) { + nxt_reg = o->as_Region(); + } + } + + if (call && + call->entry_point() == SharedRuntime::uncommon_trap_blob()->entry_point()) { + const Type* trtype = call->in(TypeFunc::Parms)->bottom_type(); + if (trtype->isa_int() && trtype->is_int()->is_con()) { + jint tr_con = trtype->is_int()->get_con(); + Deoptimization::DeoptReason reason = Deoptimization::trap_request_reason(tr_con); + Deoptimization::DeoptAction action = Deoptimization::trap_request_action(tr_con); + assert((int)reason < (int)BitsPerInt, "recode bit map"); + + if (is_set_nth_bit(C->allowed_deopt_reasons(), (int)reason) + && action != Deoptimization::Action_none) { + // This uncommon trap is sure to recompile, eventually. + // When that happens, C->too_many_traps will prevent + // this transformation from happening again. + return true; + } + } + } + + reg = nxt_reg; + cnt--; + } + + return false; +} + //============================================================================= //---------------------------State--------------------------------------------- State::State(void) { diff --git a/src/share/vm/opto/matcher.hpp b/src/share/vm/opto/matcher.hpp index 521c188a0d21472c34f129791b6d493ca295fc2e..4c805f5d792ecf8b03a1080f2096de2b71ff3b0a 100644 --- a/src/share/vm/opto/matcher.hpp +++ b/src/share/vm/opto/matcher.hpp @@ -485,6 +485,8 @@ public: // retain the Node to act as a compiler ordering barrier. static bool post_store_load_barrier(const Node* mb); + // Does n lead to an uncommon trap that can cause deoptimization? + static bool branches_to_uncommon_trap(const Node *n); #ifdef ASSERT void dump_old2new_map(); // machine-independent to machine-dependent diff --git a/src/share/vm/opto/output.cpp b/src/share/vm/opto/output.cpp index 737632a7d93fd4d3f4489b6f91d6657d8bbc22af..4fb8ba8879d22f5747b4ebc3e80332eae59ea15f 100644 --- a/src/share/vm/opto/output.cpp +++ b/src/share/vm/opto/output.cpp @@ -1459,6 +1459,12 @@ void Compile::fill_buffer(CodeBuffer* cb, uint* blk_starts) { // Intel all the time, with add-to-memory kind of opcodes. previous_offset = current_offset; } + + // Not an else-if! + // If this is a trap based cmp then add its offset to the list. + if (mach->is_TrapBasedCheckNode()) { + inct_starts[inct_cnt++] = current_offset; + } } // Verify that there is sufficient space remaining @@ -1725,6 +1731,12 @@ void Compile::FillExceptionTables(uint cnt, uint *call_returns, uint *inct_start _inc_table.append(inct_starts[inct_cnt++], blk_labels[block_num].loc_pos()); continue; } + // Handle implicit exception table updates: trap instructions. + if (n->is_Mach() && n->as_Mach()->is_TrapBasedCheckNode()) { + uint block_num = block->non_connector_successor(0)->_pre_order; + _inc_table.append(inct_starts[inct_cnt++], blk_labels[block_num].loc_pos()); + continue; + } } // End of for all blocks fill in exception table entries } diff --git a/src/share/vm/runtime/globals.hpp b/src/share/vm/runtime/globals.hpp index 3fbbaa138d3065fc52032338c741e2eb8f70e69e..0b5be1fde4b1132fa887422446c8223d146ab55f 100644 --- a/src/share/vm/runtime/globals.hpp +++ b/src/share/vm/runtime/globals.hpp @@ -2500,6 +2500,12 @@ class CommandLineFlags { develop_pd(bool, ImplicitNullChecks, \ "Generate code for implicit null checks") \ \ + product_pd(bool, TrapBasedNullChecks, \ + "Generate code for null checks that uses a cmp and trap " \ + "instruction raising SIGTRAP. This is only used if an access to" \ + "null (+offset) will not raise a SIGSEGV, i.e.," \ + "ImplicitNullChecks don't work (PPC64).") \ + \ product(bool, PrintSafepointStatistics, false, \ "Print statistics about safepoint synchronization") \ \