Projects
home:dingli:branches:openEuler:24.09-openjdk
openjdk-11
_service:tar_scm:add-LazyBox-feature.patch
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File _service:tar_scm:add-LazyBox-feature.patch of Package openjdk-11
diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index 8111a63d4..84c2817e3 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -519,6 +519,12 @@ experimental(bool, AggressiveUnboxing, false, \ "Control optimizations for aggressive boxing elimination") \ \ + experimental(bool, LazyBox, false, \ + "Delay some box operator to speed up some hot path without box ") \ + \ + experimental(bool, PrintLazyBox, false, \ + "Print LazyBox") \ + \ develop(bool, TracePostallocExpand, false, "Trace expanding nodes after" \ " register allocation.") \ \ diff --git a/src/hotspot/share/opto/callGenerator.cpp b/src/hotspot/share/opto/callGenerator.cpp index 59a02fbb1..11df8b6f5 100644 --- a/src/hotspot/share/opto/callGenerator.cpp +++ b/src/hotspot/share/opto/callGenerator.cpp @@ -150,12 +150,15 @@ JVMState* DirectCallGenerator::generate(JVMState* jvms) { } CallStaticJavaNode *call = new CallStaticJavaNode(kit.C, tf(), target, method(), kit.bci()); - if (is_inlined_method_handle_intrinsic(jvms, method())) { - // To be able to issue a direct call and skip a call to MH.linkTo*/invokeBasic adapter, - // additional information about the method being invoked should be attached - // to the call site to make resolution logic work - // (see SharedRuntime::resolve_static_call_C). - call->set_override_symbolic_info(true); + // LazyBox use DirectCallGenerator insert box node with unexpected bci + if (!(LazyBox && method()->is_boxing_method())) { + if (is_inlined_method_handle_intrinsic(jvms, method())) { + // To be able to issue a direct call and skip a call to MH.linkTo*/invokeBasic adapter, + // additional information about the method being invoked should be attached + // to the call site to make resolution logic work + // (see SharedRuntime::resolve_static_call_C). + call->set_override_symbolic_info(true); + } } _call_node = call; // Save the call node in case we need it later if (!is_static) { @@ -416,7 +419,15 @@ void LateInlineCallGenerator::do_late_inline() { // Make enough space in the expression stack to transfer // the incoming arguments and return value. - map->ensure_stack(jvms, jvms->method()->max_stack()); + uint ensure_stack_size = jvms->method()->max_stack(); + if (LazyBox && call->_is_lazy_box) { + // Make enough space for the arg pushed in insert_box_node + int arg_size = jvms->method()->arg_size(); + if (jvms->sp() + arg_size > ensure_stack_size) { + ensure_stack_size = jvms->sp() + arg_size; + } + } + map->ensure_stack(jvms, ensure_stack_size); for (uint i1 = 0; i1 < nargs; i1++) { map->set_argument(jvms, i1, call->in(TypeFunc::Parms + i1)); } @@ -442,6 +453,14 @@ void LateInlineCallGenerator::do_late_inline() { C->set_default_node_notes(entry_nn); } + if (LazyBox && call->_is_lazy_box) { + GraphKit kit(jvms); + Node* value = call->in(TypeFunc::Parms); + Node* result = kit.inline_lazy_box(call, value); + kit.replace_call(call, result, true); + return; + } + // Now perform the inlining using the synthesized JVMState JVMState* new_jvms = _inline_cg->generate(jvms); if (new_jvms == NULL) return; // no change diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp index c4064dca0..f17eda879 100644 --- a/src/hotspot/share/opto/callnode.cpp +++ b/src/hotspot/share/opto/callnode.cpp @@ -972,6 +972,10 @@ bool CallJavaNode::validate_symbolic_info() const { if (method() == NULL) { return true; // call into runtime or uncommon trap } + if (LazyBox && this->is_CallStaticJava() && + this->as_CallStaticJava()->is_boxing_method()) { + return true; + } ciMethod* symbolic_info = jvms()->method()->get_method_at_bci(_bci); ciMethod* callee = method(); if (symbolic_info->is_method_handle_intrinsic() && !callee->is_method_handle_intrinsic()) { @@ -1232,6 +1236,20 @@ void SafePointNode::grow_stack(JVMState* jvms, uint grow_by) { jvms->set_endoff(endoff + grow_by); } +void SafePointNode::desc_stack(JVMState* jvms, uint desc_by) { + assert((int)desc_by > 0, "sanity"); + int monoff = jvms->monoff(); + int scloff = jvms->scloff(); + int endoff = jvms->endoff(); + assert(endoff == (int)req(), "no other states or debug info after me"); + for (uint i = 0; i < desc_by; i++) { + del_req_ordered(monoff - 1 - i); + } + jvms->set_monoff(monoff - desc_by); + jvms->set_scloff(scloff - desc_by); + jvms->set_endoff(endoff - desc_by); +} + void SafePointNode::push_monitor(const FastLockNode *lock) { // Add a LockNode, which points to both the original BoxLockNode (the // stack space for the monitor) and the Object being locked. diff --git a/src/hotspot/share/opto/callnode.hpp b/src/hotspot/share/opto/callnode.hpp index cde33ad8b..0e07bdd62 100644 --- a/src/hotspot/share/opto/callnode.hpp +++ b/src/hotspot/share/opto/callnode.hpp @@ -331,6 +331,7 @@ public: : MultiNode( edges ), _jvms(jvms), _oop_map(NULL), + _fake_exception_state(false), _adr_type(adr_type) { init_class_id(Class_SafePoint); @@ -340,6 +341,7 @@ public: JVMState* const _jvms; // Pointer to list of JVM State objects const TypePtr* _adr_type; // What type of memory does this node produce? ReplacedNodes _replaced_nodes; // During parsing: list of pair of nodes from calls to GraphKit::replace_in_map() + bool _fake_exception_state; // lazy box may produce exception // Many calls take *all* of memory as input, // but some produce a limited subset of that memory as output. @@ -398,7 +400,13 @@ public: int grow_by = (int)stk_size - (int)jvms->stk_size(); if (grow_by > 0) grow_stack(jvms, grow_by); } + void recover_stack(JVMState* jvms, uint stk_size) { + assert(verify_jvms(jvms), "jvms must match"); + int desc_by = (int)jvms->stk_size() - (int)stk_size; + if (desc_by > 0) desc_stack(jvms, desc_by); + } void grow_stack(JVMState* jvms, uint grow_by); + void desc_stack(JVMState* jvms, uint desc_by); // Handle monitor stack void push_monitor( const FastLockNode *lock ); void pop_monitor (); @@ -708,6 +716,8 @@ public: } _is_scalar_replaceable = false; _is_non_escaping = false; + _copy_box = NULL; + _is_lazy_box = false; } CallStaticJavaNode(const TypeFunc* tf, address addr, const char* name, int bci, const TypePtr* adr_type) @@ -718,8 +728,13 @@ public: _is_scalar_replaceable = false; _is_non_escaping = false; _name = name; + _copy_box = NULL; + _is_lazy_box = false; } + Node_List* _copy_box; + bool _is_lazy_box; + // Result of Escape Analysis bool _is_scalar_replaceable; bool _is_non_escaping; diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 9c8474f18..2f188ea53 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -2237,6 +2237,11 @@ void Compile::Optimize() { print_method(PHASE_AFTER_PARSING); + if (LazyBox) { + PhaseLazyBoxOpt plb(initial_gvn()); + print_method(PHASE_LAZYBOXOPT); + } + { // Iterative Global Value Numbering, including ideal transforms // Initialize IterGVN with types and values from parse-time GVN diff --git a/src/hotspot/share/opto/doCall.cpp b/src/hotspot/share/opto/doCall.cpp index dfcc8c402..6db88965b 100644 --- a/src/hotspot/share/opto/doCall.cpp +++ b/src/hotspot/share/opto/doCall.cpp @@ -418,6 +418,49 @@ static bool check_call_consistency(JVMState* jvms, CallGenerator* cg) { } #endif // ASSERT +void Parse::do_lazybox(ciMethod* callee) { + if (!LazyBox) return; + + // unboxing method will eliminate + if (callee->is_unboxing_method()) return; + + const bool is_virtual = bc() == Bytecodes::_invokevirtual; + const bool is_static = bc() == Bytecodes::_invokestatic; + const uint nargs = callee->arg_size(); + + // receiver maybe box node + if (is_virtual) { + Node* receiver_node = stack(sp() - nargs); + if (is_box_use_node(receiver_node)) { + Node* replace = insert_box_node(receiver_node); + set_stack(sp() - nargs, replace); + return; + } + } + + // args maybe box node + if (is_virtual || is_static) { + Node* use = NULL; + uint use_idx = -1; + int count = 0; + for (uint i = nargs; i > 0; i--) { + Node* arg = stack(sp() - i); + if (is_box_use_node(arg)) { + use = arg; + use_idx = i; + count++; + } + } + + // only delay one box node to use + if (use != NULL && count == 1) { + Node* replace = insert_box_node(use); + set_stack(sp() - use_idx, replace); + return; + } + } +} + //------------------------------do_call---------------------------------------- // Handle your basic call. Inline if we can & want to, else just setup call. void Parse::do_call() { @@ -554,6 +597,13 @@ void Parse::do_call() { // It decides whether inlining is desirable or not. CallGenerator* cg = C->call_generator(callee, vtable_index, call_does_dispatch, jvms, try_inline, prof_factor(), speculative_receiver_type); + if (!cg->is_inline()) { + inc_sp(nargs); + do_lazybox(orig_callee); + dec_sp(nargs); + JVMState* jvms = sync_jvms(); + } + // NOTE: Don't use orig_callee and callee after this point! Use cg->method() instead. orig_callee = callee = NULL; diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index 6d4949de2..22222efbc 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -31,6 +31,7 @@ #include "memory/resourceArea.hpp" #include "opto/addnode.hpp" #include "opto/castnode.hpp" +#include "opto/callGenerator.hpp" #include "opto/convertnode.hpp" #include "opto/graphKit.hpp" #include "opto/idealKit.hpp" @@ -317,6 +318,15 @@ JVMState* GraphKit::transfer_exceptions_into_jvms() { return jvms; } +bool GraphKit::is_box_use_node(const Node* node) { + return node->is_Proj() && node->in(0)->is_CallStaticJava() && + node->in(0)->as_CallStaticJava()->is_boxing_method(); +} + +Node* GraphKit::replace_box_use_node(Node* n, Node* replace) { + return is_box_use_node(n) ? replace : n; +} + static inline void add_n_reqs(Node* dstphi, Node* srcphi) { assert(is_hidden_merge(dstphi), "must be a special merge node"); assert(is_hidden_merge(srcphi), "must be a special merge node"); @@ -840,6 +850,19 @@ static bool should_reexecute_implied_by_bytecode(JVMState *jvms, bool is_anewarr } } +// Delay boxnode to uncommon trap +void GraphKit::add_local(SafePointNode* call, uint idx, Node* local, GrowableArray<uint> *delay_boxes) { + if (LazyBox && local != NULL && local->is_Proj() && + local->as_Proj()->_con == TypeFunc::Parms && + local->in(0)->is_CallStaticJava() && + local->in(0)->as_CallStaticJava()->is_boxing_method() && + call->is_CallStaticJava() && + call->as_CallStaticJava()->uncommon_trap_request() != 0) { + delay_boxes->append(idx); + } + call->set_req(idx, local); +} + // Helper function for adding JVMState and debug information to node void GraphKit::add_safepoint_edges(SafePointNode* call, bool must_throw) { // Add the safepoint edges to the call (or other safepoint). @@ -847,7 +870,8 @@ void GraphKit::add_safepoint_edges(SafePointNode* call, bool must_throw) { // Make sure dead locals are set to top. This // should help register allocation time and cut down on the size // of the deoptimization information. - assert(dead_locals_are_killed(), "garbage in debug info before safepoint"); + // LazyBox may insert box in some bci, resulting in some dead locals not set to top + assert(LazyBox || dead_locals_are_killed(), "garbage in debug info before safepoint"); // Walk the inline list to fill in the correct set of JVMState's // Also fill in the associated edges for each JVMState. @@ -916,6 +940,9 @@ void GraphKit::add_safepoint_edges(SafePointNode* call, bool must_throw) { // Fill pointer walks backwards from "young:" to "root:" in the diagram above: uint debug_ptr = call->req(); + // used boxes in uncommon_trap + GrowableArray<uint> *delay_boxes = new GrowableArray<uint>(); + // Loop over the map input edges associated with jvms, add them // to the call node, & reset all offsets to match call node array. for (JVMState* in_jvms = youngest_jvms; in_jvms != NULL; ) { @@ -944,7 +971,7 @@ void GraphKit::add_safepoint_edges(SafePointNode* call, bool must_throw) { out_jvms->set_locoff(p); if (!can_prune_locals) { for (j = 0; j < l; j++) - call->set_req(p++, in_map->in(k+j)); + add_local(call, p++, in_map->in(k+j), delay_boxes); } else { p += l; // already set to top above by add_req_batch } @@ -955,7 +982,7 @@ void GraphKit::add_safepoint_edges(SafePointNode* call, bool must_throw) { out_jvms->set_stkoff(p); if (!can_prune_locals) { for (j = 0; j < l; j++) - call->set_req(p++, in_map->in(k+j)); + add_local(call, p++, in_map->in(k+j), delay_boxes); } else if (can_prune_locals && stack_slots_not_pruned != 0) { // Divide stack into {S0,...,S1}, where S0 is set to top. uint s1 = stack_slots_not_pruned; @@ -964,7 +991,8 @@ void GraphKit::add_safepoint_edges(SafePointNode* call, bool must_throw) { uint s0 = l - s1; p += s0; // skip the tops preinstalled by add_req_batch for (j = s0; j < l; j++) - call->set_req(p++, in_map->in(k+j)); + add_local(call, p++, in_map->in(k+j), delay_boxes); + } else if (can_prune_locals && stack_slots_not_pruned != 0) { } else { p += l; // already set to top above by add_req_batch } @@ -998,6 +1026,35 @@ void GraphKit::add_safepoint_edges(SafePointNode* call, bool must_throw) { in_jvms = in_jvms->caller(); } + // delay box node in uncommon_trap runtime, treat box as scalarized object + for (int index = 0; index < delay_boxes->length(); index++) { + assert(LazyBox, "delay boxnode to uncommon_trap need set LazyBox"); + assert(call->is_CallStaticJava() && + call->as_CallStaticJava()->uncommon_trap_request() != 0, "call node must be uncommon_trap call"); + uint idx = delay_boxes->at(index); + Node* local = call->in(idx); + + assert(local->in(0)->is_CallStaticJava(), "sanity"); + assert(local->in(0)->as_CallStaticJava()->is_boxing_method(), "sanity"); + + CallStaticJavaNode* box = local->in(0)->as_CallStaticJava(); + ciInstanceKlass* klass = box->method()->holder(); + int n_fields = klass->nof_nonstatic_fields(); + assert(n_fields == 1, "sanity"); + + uint first_ind = (call->req() - call->jvms()->scloff()); + Node* sobj = new SafePointScalarObjectNode(_gvn.type(local)->isa_oopptr(), +#ifdef ASSERT + NULL, +#endif // ASSERT + first_ind, n_fields); + sobj->init_req(0, C->root()); + call->add_req(local->in(0)->in(local->as_Proj()->_con)); + sobj = _gvn.transform(sobj); + call->jvms()->set_endoff(call->req()); + call->set_req(idx, sobj); + } + assert(debug_ptr == non_debug_edges, "debug info must fit exactly"); // Test the correctness of JVMState::debug_xxx accessors: @@ -4031,6 +4088,130 @@ void GraphKit::inflate_string_slow(Node* src, Node* dst, Node* start, Node* coun set_memory(st, TypeAryPtr::BYTES); } +Node* GraphKit::insert_box_node(Node* use) { + assert(is_box_use_node(use), "use node must be box_use_node"); + CallStaticJavaNode* box = use->in(0)->as_CallStaticJava(); + // get method from node + ciMethod* method = box->method(); + CallGenerator* parse_cg = CallGenerator::for_inline(method); + CallGenerator* cg = CallGenerator::for_boxing_late_inline(method, parse_cg); + + Node* arg = box->in(TypeFunc::Parms); + Node* res = NULL; + + { PreserveReexecuteState preexecs(this); + + int arg_size = method->arg_size(); + int stk_size = jvms()->stk_size(); + + assert(arg_size == 1 || arg_size == 2, "boxing method, arg size must 1 or 2!"); + + // add arg, ensure stack size + ensure_stack(sp() + arg_size); + if (arg_size == 1) { + push(arg); + } else if (arg_size == 2) { + push_pair(arg); + } + + dec_sp(arg_size); + JVMState* old_jvms = sync_jvms(); + JVMState* new_jvms = cg->generate(old_jvms); + assert(new_jvms != NULL, "insert box generate fail"); + + // add new box to late inline + CallStaticJavaNode* new_box = cg->call_node(); + new_box->_is_lazy_box = true; + + // Set the box objects and the reexecute bit for the interpreter to reexecute the bytecode if deoptimization happens + new_box->jvms()->set_should_reexecute(true); + + ciInstanceKlass* klass = box->method()->holder(); + int n_fields = klass->nof_nonstatic_fields(); + assert(n_fields == 1, "sanity"); + + uint first_ind = new_box->req() - new_box->jvms()->scloff(); + Node* sobj = new SafePointScalarObjectNode(_gvn.type(use)->isa_oopptr(), +#ifdef ASSERT + NULL, +#endif // ASSERT + first_ind, n_fields); + sobj->init_req(0, C->root()); + new_box->add_req(arg); + sobj = _gvn.transform(sobj); + new_box->jvms()->set_endoff(new_box->req()); + for (uint i = 0; i < new_box->req(); i++) { + if (new_box->in(i) == use) { + new_box->set_req(i, sobj); + } + } + + if (box->_copy_box == NULL) { + box->_copy_box = new Node_List(); + } + box->_copy_box->push(new_box); + + // inserted Box node can't throw exception to handler + // convert exception to uncommon_trap in inline_lazy_box + SafePointNode* ex_map = new_jvms->map()->next_exception(); + ex_map->_fake_exception_state = true; + add_exception_states_from(new_jvms); + + assert(new_jvms->map()->control() != top(), "generate box call node fail"); + assert(new_jvms->same_calls_as(old_jvms), "method/bci left unchanged"); + set_jvms(new_jvms); + res = pop(); + set_stack(sp(), top()); + recover_stack(stk_size); + } + return res; +} + +Node* GraphKit::inline_lazy_box(CallStaticJavaNode* box, Node* value) { + assert(box->method()->is_boxing_method(), "must be box method"); + ciInstanceKlass* klass = box->method()->holder(); + + BasicType type = klass->box_klass_type(); + + if (type == T_BOOLEAN) { + // value ? TRUE : FALSE + ciSymbol* java_lang_Boolean = ciSymbol::make("Ljava/lang/Boolean;"); + ciField* true_field = klass->get_field_by_name(ciSymbol::make("TRUE"), java_lang_Boolean, true); + ciField* false_field = klass->get_field_by_name(ciSymbol::make("FALSE"), java_lang_Boolean, true); + const TypeInstPtr* tip = TypeInstPtr::make(klass->java_mirror()); + Node* java_mirror = _gvn.makecon(tip); + Node* true_node = make_constant_from_field(true_field, java_mirror); + Node* false_node = make_constant_from_field(false_field, java_mirror); + + Node* tst = gvn().transform(Bool(CmpI(value, intcon(0)), BoolTest::ne)); + IfNode* iff = create_and_map_if(control(), tst, PROB_FAIR, COUNT_UNKNOWN); + Node* true_proj = IfTrue(iff); + Node* false_proj = IfFalse(iff); + RegionNode* region = new RegionNode(3); + gvn().set_type(region, Type::CONTROL); + region->init_req(1, true_proj); + region->init_req(2, false_proj); + Node *obj = new PhiNode(region, TypeOopPtr::make_from_klass(klass)); + gvn().set_type(obj, obj->bottom_type()); + obj->init_req(1, true_node); + obj->init_req(2, false_node); + set_control(region); + return obj; + } + + Node* kls = makecon(TypeKlassPtr::make(klass)); + Node* obj = new_instance(kls, NULL, NULL, true); + const BasicType primitive_type = klass->box_klass_type(); + int value_offset = java_lang_boxing_object::value_offset_in_bytes(primitive_type); + const TypeInstPtr* box_type = TypeInstPtr::make(TypePtr::NotNull, klass, + false, NULL, 0); + const TypePtr* value_field_type = box_type->add_offset(value_offset); + const Type *value_type = Type::get_const_basic_type(primitive_type); + access_store_at(control(), obj, basic_plus_adr(obj, value_offset), value_field_type, + value, value_type, primitive_type, IN_HEAP); + return obj; +} + Node* GraphKit::make_constant_from_field(ciField* field, Node* obj) { if (!field->is_constant()) { return NULL; // Field not marked as constant. diff --git a/src/hotspot/share/opto/graphKit.hpp b/src/hotspot/share/opto/graphKit.hpp index ef3f784d3..2d5aac53c 100644 --- a/src/hotspot/share/opto/graphKit.hpp +++ b/src/hotspot/share/opto/graphKit.hpp @@ -276,6 +276,8 @@ class GraphKit : public Phase { #ifdef ASSERT bool dead_locals_are_killed(); #endif + void add_local(SafePointNode* call, uint idx, Node* local, GrowableArray<uint> *delay_boxes); + // The call may deoptimize. Supply required JVM state as debug info. // If must_throw is true, the call is guaranteed not to return normally. void add_safepoint_edges(SafePointNode* call, @@ -478,6 +480,7 @@ class GraphKit : public Phase { void set_stack(uint idx, Node* c) { map_not_null(); _map->set_stack( _map->_jvms, idx, c); } void set_argument(uint idx, Node* c){ map_not_null(); _map->set_argument(_map->_jvms, idx, c); } void ensure_stack(uint stk_size) { map_not_null(); _map->ensure_stack(_map->_jvms, stk_size); } + void recover_stack(uint stk_size) { map_not_null(); _map->recover_stack(_map->_jvms, stk_size); } // Access unaliased memory Node* memory(uint alias_idx); @@ -863,6 +866,12 @@ class GraphKit : public Phase { void inflate_string(Node* src, Node* dst, const TypeAryPtr* dst_type, Node* count); void inflate_string_slow(Node* src, Node* dst, Node* start, Node* count); + // lazy box helpers + static bool is_box_use_node(const Node* node); + static Node* replace_box_use_node(Node* n, Node* replace); + Node* insert_box_node(Node* use); + Node* inline_lazy_box(CallStaticJavaNode* box, Node* value); + // Handy for making control flow IfNode* create_and_map_if(Node* ctrl, Node* tst, float prob, float cnt) { IfNode* iff = new IfNode(ctrl, tst, prob, cnt);// New IfNode's diff --git a/src/hotspot/share/opto/parse.hpp b/src/hotspot/share/opto/parse.hpp index aa13e8863..b16671ace 100644 --- a/src/hotspot/share/opto/parse.hpp +++ b/src/hotspot/share/opto/parse.hpp @@ -498,6 +498,9 @@ class Parse : public GraphKit { // Note: Intrinsic generation routines may be found in library_call.cpp. + // Helper function to do lazy box in do_call() + void do_lazybox(ciMethod* callee); + // Helper function to setup Ideal Call nodes void do_call(); diff --git a/src/hotspot/share/opto/parse1.cpp b/src/hotspot/share/opto/parse1.cpp index e12d1e5d3..561fe0690 100644 --- a/src/hotspot/share/opto/parse1.cpp +++ b/src/hotspot/share/opto/parse1.cpp @@ -919,7 +919,7 @@ void Parse::do_exceptions() { SafePointNode* ex_map; while ((ex_map = pop_exception_state()) != NULL) { - if (!method()->has_exception_handlers()) { + if (!method()->has_exception_handlers() || ex_map->_fake_exception_state) { // Common case: Transfer control outward. // Doing it this early allows the exceptions to common up // even between adjacent method calls. diff --git a/src/hotspot/share/opto/parse2.cpp b/src/hotspot/share/opto/parse2.cpp index 85bba6a50..027e32cec 100644 --- a/src/hotspot/share/opto/parse2.cpp +++ b/src/hotspot/share/opto/parse2.cpp @@ -2672,9 +2672,17 @@ void Parse::do_one_bytecode() { case Bytecodes::_return: return_current(NULL); break; - - case Bytecodes::_ireturn: case Bytecodes::_areturn: + a = pop(); + if (LazyBox && is_box_use_node(a)) { + push(a); + b = insert_box_node(a); + a = replace_box_use_node(a, b); + pop(); + } + return_current(a); + break; + case Bytecodes::_ireturn: case Bytecodes::_freturn: return_current(pop()); break; @@ -2761,6 +2769,19 @@ void Parse::do_one_bytecode() { maybe_add_safepoint(iter().get_dest()); a = pop(); b = pop(); + + //one of node is box use node, insert box node and replace + if (LazyBox && (is_box_use_node(a) ^ is_box_use_node(b))) { + push(b); + push(a); + Node* use = is_box_use_node(a) ? a : b; + Node* replace = insert_box_node(use); + a = replace_box_use_node(a, replace); + b = replace_box_use_node(b, replace); + pop(); + pop(); + } + c = _gvn.transform( new CmpPNode(b, a) ); c = optimize_cmp_with_klass(c); do_if(btest, c); diff --git a/src/hotspot/share/opto/parse3.cpp b/src/hotspot/share/opto/parse3.cpp index bb4d13002..0b9b14966 100644 --- a/src/hotspot/share/opto/parse3.cpp +++ b/src/hotspot/share/opto/parse3.cpp @@ -244,6 +244,14 @@ void Parse::do_put_xxx(Node* obj, ciField* field, bool is_field) { // Value to be stored Node* val = type2size[bt] == 1 ? pop() : pop_pair(); + if (LazyBox && is_box_use_node(val)) { + // oop size is 1 + push(val); + Node* replace = insert_box_node(val); + val = replace_box_use_node(val, replace); + pop(); + } + DecoratorSet decorators = IN_HEAP; decorators |= is_vol ? MO_SEQ_CST : MO_UNORDERED; diff --git a/src/hotspot/share/opto/phase.hpp b/src/hotspot/share/opto/phase.hpp index 4b0c53ffc..38683f8a7 100644 --- a/src/hotspot/share/opto/phase.hpp +++ b/src/hotspot/share/opto/phase.hpp @@ -59,6 +59,7 @@ public: Ideal_Loop, // Find idealized trip-counted loops Macro_Expand, // Expand macro nodes Peephole, // Apply peephole optimizations + LazyBoxOpt, // Apply LazyBox optimization last_phase }; diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 0a285315d..b3e2fb9b9 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -412,6 +412,217 @@ void NodeHash::operator=(const NodeHash& nh) { #endif +//============================================================================= +//------------------------------PhaseLazyBoxOpt------------------------------ +// Phase that first collecting all the box nodes, if use nodes of a box node are +// all SafePointNode or it's subclass and the usage is not monitor, scalar and args, then eliminate the box node. +// else, eliminate the related inserted box node. +PhaseLazyBoxOpt::PhaseLazyBoxOpt(PhaseGVN *gvn, PhaseNumber phase_num) + : Phase(phase_num), + _gvn(gvn) { + if (PrintLazyBox) { + tty->print("LazyBoxOpt for method: %s.%s\n", C->method()->holder()->name()->as_utf8(), C->method()->name()->as_utf8()); + } + Node_List box_nodes = collect_box_nodes(); + for (uint i = 0; i < box_nodes.size(); i++) { + CallStaticJavaNode* box_node = (CallStaticJavaNode*)box_nodes[i]; + if (is_eliminatable(box_node)) { + // delete origin box node + if (PrintLazyBox) { + tty->print("delete origin box node %d\n", box_node->_idx); + } + + // if copy box not used, remove it + if (box_node->_copy_box != NULL) { + for (uint j = 0; j < box_node->_copy_box->size(); j++) { + CallStaticJavaNode* n = (CallStaticJavaNode*)box_node->_copy_box->at(j); + Node* resproj = n->proj_out_or_null(TypeFunc::Parms); + if (resproj == NULL) { + eliminate_call(n); + } + } + } + + eliminate_call(box_node); + } else if (box_node->_copy_box != NULL) { + // delete copy box node + if (PrintLazyBox) { + tty->print("delete copy box node for box node %d \n", box_node->_idx); + } + Node* origin_resproj = box_node->proj_out_or_null(TypeFunc::Parms); + + for (uint j = 0; j < box_node->_copy_box->size(); j++) { + CallStaticJavaNode* n = (CallStaticJavaNode*)box_node->_copy_box->at(j); + if (PrintLazyBox) { + tty->print(" delete copy box node %d\n", n->_idx); + } + // replace the copy box node resproj by original box node resproj + Node* copy_resproj = n->proj_out_or_null(TypeFunc::Parms); + + if (copy_resproj != NULL) { + C->gvn_replace_by(copy_resproj, origin_resproj); + } + eliminate_call(n); + } + } + } + + if (C->failing()) return; + { + ResourceMark rm; + PhaseRemoveUseless pru(C->initial_gvn(), C->for_igvn()); + } +} + +Node_List PhaseLazyBoxOpt::collect_box_nodes() { + Node_List box_nodes; + + Node* start_node = C->start(); + Node* framptr_node = start_node->raw_out(TypeFunc::FramePtr + 1); + + for (DUIterator_Fast imax, i = framptr_node->fast_outs(imax); i < imax; i++) { + Node* m = framptr_node->fast_out(i); + if (m->is_CallStaticJava()) { + ciMethod* method = m->as_CallStaticJava()->method(); + if (method != NULL && method->is_boxing_method() && m->as_CallStaticJava()->_is_lazy_box == false) { + box_nodes.push(m); + } + } + } + return box_nodes; +} + +// determine if a box node is eliminatable +bool PhaseLazyBoxOpt::is_eliminatable(CallStaticJavaNode* box_node) { + assert(!box_node->_is_lazy_box, "must orgin box node"); + + Node* resproj = box_node->proj_out_or_null(TypeFunc::Parms); + if (resproj == NULL) { + return true; + } + + for (DUIterator_Fast imax, i = resproj->fast_outs(imax); i < imax; i++) { + Node* res = resproj; + Node* use_node = res->fast_out(i); + if (use_node->is_SafePoint()) { + int arg_end = 0; + JVMState* youngest_jvms = use_node->jvms(); + for (JVMState* jvms = youngest_jvms; jvms != NULL; ) { + arg_end = jvms->locoff(); + + int k, l; + // if origin box node is used only in SafePoint's stack and local, so it's not used actually + // check local, just check + k = jvms->locoff(); + l = jvms->loc_size(); + for (int j = 0; j < l; j++){ + if (use_node->in(k+j) == res) { +#ifndef PRODUCT + if (PrintLazyBox) { + tty->print("use node is safepoint, check local!\n"); + use_node->dump(0); + } +#endif + } + } + + // check stack, just check + k = jvms->stkoff(); + l = jvms->sp(); + for (int j = 0; j < l; j++) { + if (use_node->in(k+j) == res) { +#ifndef PRODUCT + if (PrintLazyBox) { + tty->print("use node is safepoint, check stack!\n"); + use_node->dump(0); + } +#endif + } + } + // check monitor + k = jvms->monoff(); + l = jvms->mon_size(); + for (int j = 0; j < l; j++) { + if (use_node->in(k+j) == res) { +#ifndef PRODUCT + if (PrintLazyBox) { + tty->print("use node is safepoint, check monitor false!\n"); + use_node->dump(0); + } +#endif + return false; + } + } + // check scalar + k = jvms->scloff(); + l = jvms->scl_size(); + for (int j = 0; j < l; j++) { + if (use_node->in(k+j) == res) { +#ifndef PRODUCT + if (PrintLazyBox) { + tty->print("use node is safepoint, check scalar false!\n"); + use_node->dump(0); + } +#endif + return false; + } + } + jvms = jvms->caller(); + } + + if (use_node->is_CallJava()) { + for (int i = TypeFunc::Parms; i < arg_end; i++) { + if(use_node->in(i) == res) { +#ifndef PRODUCT + if (PrintLazyBox) { + tty->print("use node is CallJava, check args false!\n"); + use_node->dump(0); + } +#endif + return false; + } + } + } + + } else { +#ifndef PRODUCT + if (PrintLazyBox) { + tty->print("use node %s can't do lazybox!\n", use_node->Name()); + use_node->dump(0); + } +#endif + return false; + } + } + return true; +} + +void PhaseLazyBoxOpt::eliminate_call(CallNode* call) { + CallProjections projs; + call->extract_projections(&projs, false); + if (projs.fallthrough_catchproj != NULL) { + C->gvn_replace_by(projs.fallthrough_catchproj, call->in(TypeFunc::Control)); + } + if (projs.fallthrough_memproj != NULL) { + C->gvn_replace_by(projs.fallthrough_memproj, call->in(TypeFunc::Memory)); + } + if (projs.catchall_memproj != NULL) { + C->gvn_replace_by(projs.catchall_memproj, C->top()); + } + if (projs.fallthrough_ioproj != NULL) { + C->gvn_replace_by(projs.fallthrough_ioproj, call->in(TypeFunc::I_O)); + } + if (projs.catchall_ioproj != NULL) { + C->gvn_replace_by(projs.catchall_ioproj, C->top()); + } + if (projs.catchall_catchproj != NULL) { + C->gvn_replace_by(projs.catchall_catchproj, C->top()); + } + if (projs.resproj != NULL) { + C->gvn_replace_by(projs.resproj, C->top()); + } + C->gvn_replace_by(call, C->top()); +} //============================================================================= //------------------------------PhaseRemoveUseless----------------------------- diff --git a/src/hotspot/share/opto/phaseX.hpp b/src/hotspot/share/opto/phaseX.hpp index 0591e9807..5a55b75e2 100644 --- a/src/hotspot/share/opto/phaseX.hpp +++ b/src/hotspot/share/opto/phaseX.hpp @@ -173,6 +173,26 @@ public: PhaseNumber phase_num = Remove_Useless_And_Renumber_Live); }; +//------------------------------PhaseLazyBoxOpt------------------------------ +// Phase that first collecting all the box nodes, if use nodes of a box node are +// all SafePointNode or it's subclass and the usage is not monitor, scalar and args, then eliminate the box node. +// else, eliminate the related inserted box node. +class PhaseLazyBoxOpt : public Phase { +private: + PhaseGVN* _gvn; + +public: + PhaseLazyBoxOpt(PhaseGVN *gvn, PhaseNumber phase_num = LazyBoxOpt); + + // collecting a list of box nodes + Node_List collect_box_nodes(); + + // determine if a box node is eliminatable + bool is_eliminatable(CallStaticJavaNode* box_node); + + void eliminate_call(CallNode* call); +}; + //------------------------------PhaseTransform--------------------------------- // Phases that analyze, then transform. Constructing the Phase object does any diff --git a/src/hotspot/share/opto/phasetype.hpp b/src/hotspot/share/opto/phasetype.hpp index 100db1773..7e57cfb07 100644 --- a/src/hotspot/share/opto/phasetype.hpp +++ b/src/hotspot/share/opto/phasetype.hpp @@ -30,6 +30,7 @@ enum CompilerPhaseType { PHASE_AFTER_STRINGOPTS, PHASE_BEFORE_REMOVEUSELESS, PHASE_AFTER_PARSING, + PHASE_LAZYBOXOPT, PHASE_ITER_GVN1, PHASE_PHASEIDEAL_BEFORE_EA, PHASE_ITER_GVN_AFTER_EA, @@ -73,6 +74,7 @@ class CompilerPhaseTypeHelper { case PHASE_AFTER_STRINGOPTS: return "After StringOpts"; case PHASE_BEFORE_REMOVEUSELESS: return "Before RemoveUseless"; case PHASE_AFTER_PARSING: return "After Parsing"; + case PHASE_LAZYBOXOPT: return "After LazyBoxOpt"; case PHASE_ITER_GVN1: return "Iter GVN 1"; case PHASE_PHASEIDEAL_BEFORE_EA: return "PhaseIdealLoop before EA"; case PHASE_ITER_GVN_AFTER_EA: return "Iter GVN after EA"; diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 3e7d48ad5..baf221ec4 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -1969,6 +1969,19 @@ jint Arguments::set_aggressive_opts_flags() { AggressiveUnboxing = false; } } + + // LazyBox need set AggressiveUnboxing + if (LazyBox) { + if (!AggressiveUnboxing || UseAOT || EnableJVMCI) { + warning("LazyBox is disable because AggressiveUnboxing is disabled or UseAOT/EnableJVMCI is enable"); + LazyBox = false; + } + } + + if (PrintLazyBox) { + PrintLazyBox = LazyBox; + } + if (AggressiveOpts || !FLAG_IS_DEFAULT(AutoBoxCacheMax)) { if (FLAG_IS_DEFAULT(EliminateAutoBox)) { FLAG_SET_DEFAULT(EliminateAutoBox, true); diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/opto/CompilerPhaseType.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/opto/CompilerPhaseType.java index e0eb10942..0478c56d9 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/opto/CompilerPhaseType.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/opto/CompilerPhaseType.java @@ -30,6 +30,7 @@ public enum CompilerPhaseType { PHASE_BEFORE_STRINGOPTS ("Before StringOpts"), PHASE_AFTER_STRINGOPTS ("After StringOpts"), PHASE_BEFORE_REMOVEUSELESS ("Before RemoveUseless"), + PHASE_LAZYBOXOPT ("After LazyBoxOpt"), PHASE_AFTER_PARSING ("After Parsing"), PHASE_ITER_GVN1 ("Iter GVN 1"), PHASE_PHASEIDEAL_BEFORE_EA ("PhaseIdealLoop before EA"), diff --git a/test/hotspot/jtreg/compiler/lazybox/TestLazyBox.java b/test/hotspot/jtreg/compiler/lazybox/TestLazyBox.java new file mode 100644 index 000000000..4d9a72db2 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lazybox/TestLazyBox.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2021, Huawei Technologies Co. Ltd. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @summary Test whether LazyBox works + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+LazyBox + * -XX:+AggressiveUnboxing + * -XX:CompileCommand=dontinline,compiler/lazybox/TestLazyBox.foo + * -XX:CompileCommand=dontinline,compiler/lazybox/TestLazyBox.black + * -Xcomp -XX:-TieredCompilation + * compiler.lazybox.TestLazyBox + */ + +package compiler.lazybox; + +class Wrap { + public Object field; +} + +public class TestLazyBox { + public static Integer base = null; + + public static void main(String[] args) throws Exception{ + for(int i = 0; i < 10; i++) { + Integer IntObj2 = Integer.valueOf(i); + foo(IntObj2); + } + System.out.println("Passed"); + } + + public static void foo(Integer IntObj2) throws Exception { + + Integer IntObj = Integer.valueOf(299); + base = IntObj; + try{ + black(IntObj); + + if (IntObj == IntObj2) { + black(IntObj); + } + + if (IntObj.equals(IntObj2)) { + black(IntObj); + } + + Wrap wrap = new Wrap(); + wrap.field = IntObj; + + black(wrap.field); + } catch(Exception e) { + e.printStackTrace(); + } + } + + public static void black(Object i) { + if (base == i) { + System.err.println("Failed tests."); + throw new InternalError(); + } + } +} -- 2.19.0
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.
浙ICP备2022010568号-2