diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp index 1bc5697c8ec..727dacb0e2f 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp @@ -3069,6 +3069,9 @@ void LIR_Assembler::emit_profile_type(LIR_OpProfileType* op) { COMMENT("} emit_profile_type"); } +void LIR_Assembler::emit_profile_inline_type(LIR_OpProfileInlineType* op) { + Unimplemented(); +} void LIR_Assembler::align_backward_branch_target() { } diff --git a/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp b/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp index 7b0794afc9f..e9daefe2e36 100644 --- a/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp +++ b/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp @@ -2563,6 +2563,10 @@ void LIR_Assembler::emit_profile_type(LIR_OpProfileType* op) { fatal("Type profiling not implemented on this platform"); } +void LIR_Assembler::emit_profile_inline_type(LIR_OpProfileInlineType* op) { + Unimplemented(); +} + void LIR_Assembler::emit_delay(LIR_OpDelay*) { Unimplemented(); } diff --git a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp index 54e79f9d4bd..16240fdac41 100644 --- a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp @@ -3199,6 +3199,9 @@ void LIR_Assembler::emit_profile_type(LIR_OpProfileType* op) { __ bind(Ldone); } +void LIR_Assembler::emit_profile_inline_type(LIR_OpProfileInlineType* op) { + Unimplemented(); +} void LIR_Assembler::emit_updatecrc32(LIR_OpUpdateCRC32* op) { assert(op->crc()->is_single_cpu(), "crc must be register"); diff --git a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp index 24c8178f1dc..0b40f81f768 100644 --- a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp @@ -3088,6 +3088,10 @@ void LIR_Assembler::emit_profile_type(LIR_OpProfileType* op) { } } +void LIR_Assembler::emit_profile_inline_type(LIR_OpProfileInlineType* op) { + Unimplemented(); +} + void LIR_Assembler::emit_updatecrc32(LIR_OpUpdateCRC32* op) { assert(op->crc()->is_single_cpu(), "crc must be register"); assert(op->val()->is_single_cpu(), "byte value must be register"); diff --git a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp index eb06e97cea5..334af0a1484 100644 --- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp @@ -3982,6 +3982,26 @@ void LIR_Assembler::emit_profile_type(LIR_OpProfileType* op) { } } +void LIR_Assembler::emit_profile_inline_type(LIR_OpProfileInlineType* op) { + Register obj = op->obj()->as_register(); + Register tmp = op->tmp()->as_pointer_register(); + Address mdo_addr = as_Address(op->mdp()->as_address_ptr()); + bool not_null = op->not_null(); + int flag = op->flag(); + + Label not_inline_type; + if (!not_null) { + __ testptr(obj, obj); + __ jccb(Assembler::zero, not_inline_type); + } + + __ test_oop_is_not_inline_type(obj, tmp, not_inline_type); + + __ orb(mdo_addr, flag); + + __ bind(not_inline_type); +} + void LIR_Assembler::emit_delay(LIR_OpDelay*) { Unimplemented(); } diff --git a/src/hotspot/cpu/x86/interp_masm_x86.cpp b/src/hotspot/cpu/x86/interp_masm_x86.cpp index 7d7a77f25c8..f1312ac13b3 100644 --- a/src/hotspot/cpu/x86/interp_masm_x86.cpp +++ b/src/hotspot/cpu/x86/interp_masm_x86.cpp @@ -1695,7 +1695,7 @@ void InterpreterMacroAssembler::profile_taken_branch(Register mdp, } -void InterpreterMacroAssembler::profile_not_taken_branch(Register mdp) { +void InterpreterMacroAssembler::profile_not_taken_branch(Register mdp, bool acmp) { if (ProfileInterpreter) { Label profile_continue; @@ -1707,7 +1707,7 @@ void InterpreterMacroAssembler::profile_not_taken_branch(Register mdp) { // The method data pointer needs to be updated to correspond to // the next bytecode - update_mdp_by_constant(mdp, in_bytes(BranchData::branch_data_size())); + update_mdp_by_constant(mdp, acmp ? in_bytes(ACmpData::acmp_data_size()): in_bytes(BranchData::branch_data_size())); bind(profile_continue); } } @@ -2131,6 +2131,37 @@ void InterpreterMacroAssembler::profile_element(Register mdp, } } +void InterpreterMacroAssembler::profile_acmp(Register mdp, + Register left, + Register right, + Register tmp) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(mdp, profile_continue); + + mov(tmp, left); + profile_obj_type(tmp, Address(mdp, in_bytes(ACmpData::left_offset()))); + + Label left_not_inline_type; + test_oop_is_not_inline_type(left, tmp, left_not_inline_type); + set_mdp_flag_at(mdp, ACmpData::left_inline_type_byte_constant()); + bind(left_not_inline_type); + + mov(tmp, right); + profile_obj_type(tmp, Address(mdp, in_bytes(ACmpData::right_offset()))); + + Label right_not_inline_type; + test_oop_is_not_inline_type(right, tmp, right_not_inline_type); + set_mdp_flag_at(mdp, ACmpData::right_inline_type_byte_constant()); + bind(right_not_inline_type); + + bind(profile_continue); + } +} + + void InterpreterMacroAssembler::_interp_verify_oop(Register reg, TosState state, const char* file, int line) { if (state == atos) { MacroAssembler::_verify_oop(reg, "broken oop", file, line); diff --git a/src/hotspot/cpu/x86/interp_masm_x86.hpp b/src/hotspot/cpu/x86/interp_masm_x86.hpp index 5c43c28e6aa..0ea91e0b988 100644 --- a/src/hotspot/cpu/x86/interp_masm_x86.hpp +++ b/src/hotspot/cpu/x86/interp_masm_x86.hpp @@ -286,7 +286,7 @@ class InterpreterMacroAssembler: public MacroAssembler { void update_mdp_for_ret(Register return_bci); void profile_taken_branch(Register mdp, Register bumped_count); - void profile_not_taken_branch(Register mdp); + void profile_not_taken_branch(Register mdp, bool acmp = false); void profile_call(Register mdp); void profile_final_call(Register mdp); void profile_virtual_call(Register receiver, Register mdp, @@ -301,6 +301,7 @@ class InterpreterMacroAssembler: public MacroAssembler { Register scratch2); void profile_array(Register mdp, Register array, Register tmp); void profile_element(Register mdp, Register element, Register tmp); + void profile_acmp(Register mdp, Register left, Register right, Register tmp); // Debugging // only if +VerifyOops && state == atos diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index b65a8270446..b31ef4b74f3 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -2616,6 +2616,16 @@ void MacroAssembler::test_klass_is_inline_type(Register klass, Register temp_reg jcc(Assembler::notZero, is_inline_type); } +void MacroAssembler::test_oop_is_not_inline_type(Register object, Register tmp, Label& not_inline_type) { + testptr(object, object); + jcc(Assembler::equal, not_inline_type); + const int is_inline_type_mask = markWord::always_locked_pattern; + movptr(tmp, Address(object, oopDesc::mark_offset_in_bytes())); + andptr(tmp, is_inline_type_mask); + cmpptr(tmp, is_inline_type_mask); + jcc(Assembler::notEqual, not_inline_type); +} + void MacroAssembler::test_klass_is_empty_inline_type(Register klass, Register temp_reg, Label& is_empty_inline_type) { #ifdef ASSERT { diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.hpp b/src/hotspot/cpu/x86/macroAssembler_x86.hpp index 9145fb95056..daf524641e8 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp @@ -104,6 +104,7 @@ class MacroAssembler: public Assembler { // valueKlass queries, kills temp_reg void test_klass_is_inline_type(Register klass, Register temp_reg, Label& is_inline_type); void test_klass_is_empty_inline_type(Register klass, Register temp_reg, Label& is_empty_inline_type); + void test_oop_is_not_inline_type(Register object, Register tmp, Label& not_inline_type); // Get the default value oop for the given InlineKlass void get_default_value_oop(Register inline_klass, Register temp_reg, Register obj); diff --git a/src/hotspot/cpu/x86/templateTable_x86.cpp b/src/hotspot/cpu/x86/templateTable_x86.cpp index 739460ffb6d..6d534d59239 100644 --- a/src/hotspot/cpu/x86/templateTable_x86.cpp +++ b/src/hotspot/cpu/x86/templateTable_x86.cpp @@ -2482,24 +2482,22 @@ void TemplateTable::if_acmp(Condition cc) { Label taken, not_taken; __ pop_ptr(rdx); + __ profile_acmp(rbx, rdx, rax, rcx); + const int is_inline_type_mask = markWord::always_locked_pattern; if (EnableValhalla) { __ cmpoop(rdx, rax); __ jcc(Assembler::equal, (cc == equal) ? taken : not_taken); // might be substitutable, test if either rax or rdx is null - __ movptr(rbx, rdx); - __ andptr(rbx, rax); - __ testptr(rbx, rbx); + __ testptr(rdx, rax); __ jcc(Assembler::zero, (cc == equal) ? not_taken : taken); // and both are values ? __ movptr(rbx, Address(rdx, oopDesc::mark_offset_in_bytes())); + __ andptr(rbx, Address(rax, oopDesc::mark_offset_in_bytes())); __ andptr(rbx, is_inline_type_mask); - __ movptr(rcx, Address(rax, oopDesc::mark_offset_in_bytes())); - __ andptr(rbx, is_inline_type_mask); - __ andptr(rbx, rcx); - __ cmpl(rbx, is_inline_type_mask); + __ cmpptr(rbx, is_inline_type_mask); __ jcc(Assembler::notEqual, (cc == equal) ? not_taken : taken); // same value klass ? @@ -2522,7 +2520,7 @@ void TemplateTable::if_acmp(Condition cc) { __ bind(taken); branch(false, false); __ bind(not_taken); - __ profile_not_taken_branch(rax); + __ profile_not_taken_branch(rax, true); } void TemplateTable::invoke_is_substitutable(Register aobj, Register bobj, diff --git a/src/hotspot/share/c1/c1_Canonicalizer.cpp b/src/hotspot/share/c1/c1_Canonicalizer.cpp index 38fde34cac4..f3a4e9ec032 100644 --- a/src/hotspot/share/c1/c1_Canonicalizer.cpp +++ b/src/hotspot/share/c1/c1_Canonicalizer.cpp @@ -1055,6 +1055,7 @@ void Canonicalizer::do_UnsafeGetAndSetObject(UnsafeGetAndSetObject* x) {} void Canonicalizer::do_ProfileCall(ProfileCall* x) {} void Canonicalizer::do_ProfileReturnType(ProfileReturnType* x) {} void Canonicalizer::do_ProfileInvoke(ProfileInvoke* x) {} +void Canonicalizer::do_ProfileACmpTypes(ProfileACmpTypes* x) {} void Canonicalizer::do_RuntimeCall(RuntimeCall* x) {} void Canonicalizer::do_RangeCheckPredicate(RangeCheckPredicate* x) {} #ifdef ASSERT diff --git a/src/hotspot/share/c1/c1_Canonicalizer.hpp b/src/hotspot/share/c1/c1_Canonicalizer.hpp index 2e96854a324..ab4f60e144e 100644 --- a/src/hotspot/share/c1/c1_Canonicalizer.hpp +++ b/src/hotspot/share/c1/c1_Canonicalizer.hpp @@ -110,6 +110,7 @@ class Canonicalizer: InstructionVisitor { virtual void do_UnsafeGetAndSetObject(UnsafeGetAndSetObject* x); virtual void do_ProfileCall (ProfileCall* x); virtual void do_ProfileReturnType (ProfileReturnType* x); + virtual void do_ProfileACmpTypes(ProfileACmpTypes* x); virtual void do_ProfileInvoke (ProfileInvoke* x); virtual void do_RuntimeCall (RuntimeCall* x); virtual void do_MemBar (MemBar* x); diff --git a/src/hotspot/share/c1/c1_GraphBuilder.cpp b/src/hotspot/share/c1/c1_GraphBuilder.cpp index b9b5494c15d..58afe936553 100644 --- a/src/hotspot/share/c1/c1_GraphBuilder.cpp +++ b/src/hotspot/share/c1/c1_GraphBuilder.cpp @@ -1353,6 +1353,11 @@ void GraphBuilder::if_node(Value x, If::Condition cond, Value y, ValueStack* sta } } } + if ((stream()->cur_bc() == Bytecodes::_if_acmpeq || stream()->cur_bc() == Bytecodes::_if_acmpne) && + is_profiling() && profile_branches()) { + compilation()->set_would_profile(true); + append(new ProfileACmpTypes(method(), bci(), x, y)); + } // In case of loop invariant code motion or predicate insertion // before the body of a loop the state is needed diff --git a/src/hotspot/share/c1/c1_Instruction.hpp b/src/hotspot/share/c1/c1_Instruction.hpp index 9c5870bc78f..4576b241237 100644 --- a/src/hotspot/share/c1/c1_Instruction.hpp +++ b/src/hotspot/share/c1/c1_Instruction.hpp @@ -108,6 +108,7 @@ class UnsafePutObject; class UnsafeGetAndSetObject; class ProfileCall; class ProfileReturnType; +class ProfileACmpTypes; class ProfileInvoke; class RuntimeCall; class MemBar; @@ -210,6 +211,7 @@ class InstructionVisitor: public StackObj { virtual void do_UnsafeGetAndSetObject(UnsafeGetAndSetObject* x) = 0; virtual void do_ProfileCall (ProfileCall* x) = 0; virtual void do_ProfileReturnType (ProfileReturnType* x) = 0; + virtual void do_ProfileACmpTypes(ProfileACmpTypes* x) = 0; virtual void do_ProfileInvoke (ProfileInvoke* x) = 0; virtual void do_RuntimeCall (RuntimeCall* x) = 0; virtual void do_MemBar (MemBar* x) = 0; @@ -2681,7 +2683,7 @@ LEAF(ProfileReturnType, Instruction) , _ret(ret) { set_needs_null_check(true); - // The ProfileType has side-effects and must occur precisely where located + // The ProfileReturnType has side-effects and must occur precisely where located pin(); } @@ -2697,6 +2699,48 @@ LEAF(ProfileReturnType, Instruction) } }; +LEAF(ProfileACmpTypes, Instruction) + private: + ciMethod* _method; + int _bci; + Value _left; + Value _right; + bool _left_maybe_null; + bool _right_maybe_null; + + public: + ProfileACmpTypes(ciMethod* method, int bci, Value left, Value right) + : Instruction(voidType) + , _method(method) + , _bci(bci) + , _left(left) + , _right(right) + { + // The ProfileACmp has side-effects and must occur precisely where located + pin(); + _left_maybe_null = true; + _right_maybe_null = true; + } + + ciMethod* method() const { return _method; } + int bci() const { return _bci; } + Value left() const { return _left; } + Value right() const { return _right; } + bool left_maybe_null() const { return _left_maybe_null; } + bool right_maybe_null() const { return _right_maybe_null; } + void set_left_maybe_null(bool v) { _left_maybe_null = v; } + void set_right_maybe_null(bool v) { _right_maybe_null = v; } + + virtual void input_values_do(ValueVisitor* f) { + if (_left != NULL) { + f->visit(&_left); + } + if (_right != NULL) { + f->visit(&_right); + } + } +}; + // Call some C runtime function that doesn't safepoint, // optionally passing the current thread as the first argument. LEAF(RuntimeCall, Instruction) diff --git a/src/hotspot/share/c1/c1_InstructionPrinter.cpp b/src/hotspot/share/c1/c1_InstructionPrinter.cpp index c72b693cc04..1570dabd264 100644 --- a/src/hotspot/share/c1/c1_InstructionPrinter.cpp +++ b/src/hotspot/share/c1/c1_InstructionPrinter.cpp @@ -916,6 +916,7 @@ void InstructionPrinter::do_ProfileReturnType(ProfileReturnType* x) { output()->print(" %s.%s", x->method()->holder()->name()->as_utf8(), x->method()->name()->as_utf8()); output()->put(')'); } + void InstructionPrinter::do_ProfileInvoke(ProfileInvoke* x) { output()->print("profile_invoke "); output()->print(" %s.%s", x->inlinee()->holder()->name()->as_utf8(), x->inlinee()->name()->as_utf8()); @@ -923,6 +924,13 @@ void InstructionPrinter::do_ProfileInvoke(ProfileInvoke* x) { } +void InstructionPrinter::do_ProfileACmpTypes(ProfileACmpTypes* x) { + output()->print("profile acmp types "); + print_value(x->left()); + output()->print(", "); + print_value(x->right()); +} + void InstructionPrinter::do_RuntimeCall(RuntimeCall* x) { output()->print("call_rt %s(", x->entry_name()); for (int i = 0; i < x->number_of_arguments(); i++) { diff --git a/src/hotspot/share/c1/c1_InstructionPrinter.hpp b/src/hotspot/share/c1/c1_InstructionPrinter.hpp index 28ab29a05fe..de18da4bddf 100644 --- a/src/hotspot/share/c1/c1_InstructionPrinter.hpp +++ b/src/hotspot/share/c1/c1_InstructionPrinter.hpp @@ -134,6 +134,7 @@ class InstructionPrinter: public InstructionVisitor { virtual void do_UnsafeGetAndSetObject(UnsafeGetAndSetObject* x); virtual void do_ProfileCall (ProfileCall* x); virtual void do_ProfileReturnType (ProfileReturnType* x); + virtual void do_ProfileACmpTypes(ProfileACmpTypes* x); virtual void do_ProfileInvoke (ProfileInvoke* x); virtual void do_RuntimeCall (RuntimeCall* x); virtual void do_MemBar (MemBar* x); diff --git a/src/hotspot/share/c1/c1_LIR.cpp b/src/hotspot/share/c1/c1_LIR.cpp index c174be828fd..0a18063234c 100644 --- a/src/hotspot/share/c1/c1_LIR.cpp +++ b/src/hotspot/share/c1/c1_LIR.cpp @@ -974,7 +974,18 @@ void LIR_OpVisitState::visit(LIR_Op* op) { do_temp(opProfileType->_tmp); break; } - default: + + // LIR_OpProfileInlineType: + case lir_profile_inline_type: { + assert(op->as_OpProfileInlineType() != NULL, "must be"); + LIR_OpProfileInlineType* opProfileInlineType = (LIR_OpProfileInlineType*)op; + + do_input(opProfileInlineType->_mdp); do_temp(opProfileInlineType->_mdp); + do_input(opProfileInlineType->_obj); + do_temp(opProfileInlineType->_tmp); + break; + } +default: op->visit(this); } } @@ -1191,6 +1202,10 @@ void LIR_OpProfileType::emit_code(LIR_Assembler* masm) { masm->emit_profile_type(this); } +void LIR_OpProfileInlineType::emit_code(LIR_Assembler* masm) { + masm->emit_profile_inline_type(this); +} + // LIR_List LIR_List::LIR_List(Compilation* compilation, BlockBegin* block) : _operations(8) @@ -1877,6 +1892,8 @@ const char * LIR_Op::name() const { case lir_profile_call: s = "profile_call"; break; // LIR_OpProfileType case lir_profile_type: s = "profile_type"; break; + // LIR_OpProfileInlineType + case lir_profile_inline_type: s = "profile_inline_type"; break; // LIR_OpAssert #ifdef ASSERT case lir_assert: s = "assert"; break; @@ -2213,6 +2230,14 @@ void LIR_OpProfileType::print_instr(outputStream* out) const { tmp()->print(out); out->print(" "); } +// LIR_OpProfileInlineType +void LIR_OpProfileInlineType::print_instr(outputStream* out) const { + out->print(" flag = %x ", flag()); + mdp()->print(out); out->print(" "); + obj()->print(out); out->print(" "); + tmp()->print(out); out->print(" "); +} + #endif // PRODUCT // Implementation of LIR_InsertionBuffer diff --git a/src/hotspot/share/c1/c1_LIR.hpp b/src/hotspot/share/c1/c1_LIR.hpp index 2471925ac48..316b459a3f9 100644 --- a/src/hotspot/share/c1/c1_LIR.hpp +++ b/src/hotspot/share/c1/c1_LIR.hpp @@ -878,6 +878,7 @@ class LIR_OpSubstitutabilityCheck; class LIR_OpCompareAndSwap; class LIR_OpProfileCall; class LIR_OpProfileType; +class LIR_OpProfileInlineType; #ifdef ASSERT class LIR_OpAssert; #endif @@ -1000,6 +1001,7 @@ enum LIR_Code { , begin_opMDOProfile , lir_profile_call , lir_profile_type + , lir_profile_inline_type , end_opMDOProfile , begin_opAssert , lir_assert @@ -1148,6 +1150,7 @@ class LIR_Op: public CompilationResourceObj { virtual LIR_OpCompareAndSwap* as_OpCompareAndSwap() { return NULL; } virtual LIR_OpProfileCall* as_OpProfileCall() { return NULL; } virtual LIR_OpProfileType* as_OpProfileType() { return NULL; } + virtual LIR_OpProfileInlineType* as_OpProfileInlineType() { return NULL; } #ifdef ASSERT virtual LIR_OpAssert* as_OpAssert() { return NULL; } #endif @@ -2056,6 +2059,38 @@ class LIR_OpProfileType : public LIR_Op { virtual void print_instr(outputStream* out) const PRODUCT_RETURN; }; +// LIR_OpProfileInlineType +class LIR_OpProfileInlineType : public LIR_Op { + friend class LIR_OpVisitState; + + private: + LIR_Opr _mdp; + LIR_Opr _obj; + int _flag; + LIR_Opr _tmp; + bool _not_null; // true if we know statically that _obj cannot be null + + public: + // Destroys recv + LIR_OpProfileInlineType(LIR_Opr mdp, LIR_Opr obj, int flag, LIR_Opr tmp, bool not_null) + : LIR_Op(lir_profile_inline_type, LIR_OprFact::illegalOpr, NULL) // no result, no info + , _mdp(mdp) + , _obj(obj) + , _flag(flag) + , _tmp(tmp) + , _not_null(not_null) { } + + LIR_Opr mdp() const { return _mdp; } + LIR_Opr obj() const { return _obj; } + int flag() const { return _flag; } + LIR_Opr tmp() const { return _tmp; } + bool not_null() const { return _not_null; } + + virtual void emit_code(LIR_Assembler* masm); + virtual LIR_OpProfileInlineType* as_OpProfileInlineType() { return this; } + virtual void print_instr(outputStream* out) const PRODUCT_RETURN; +}; + class LIR_InsertionBuffer; //--------------------------------LIR_List--------------------------------------------------- @@ -2349,6 +2384,9 @@ class LIR_List: public CompilationResourceObj { void profile_type(LIR_Address* mdp, LIR_Opr obj, ciKlass* exact_klass, intptr_t current_klass, LIR_Opr tmp, bool not_null, bool no_conflict) { append(new LIR_OpProfileType(LIR_OprFact::address(mdp), obj, exact_klass, current_klass, tmp, not_null, no_conflict)); } + void profile_inline_type(LIR_Address* mdp, LIR_Opr obj, int flag, LIR_Opr tmp, bool not_null) { + append(new LIR_OpProfileInlineType(LIR_OprFact::address(mdp), obj, flag, tmp, not_null)); + } void xadd(LIR_Opr src, LIR_Opr add, LIR_Opr res, LIR_Opr tmp) { append(new LIR_Op2(lir_xadd, src, add, res, tmp)); } void xchg(LIR_Opr src, LIR_Opr set, LIR_Opr res, LIR_Opr tmp) { append(new LIR_Op2(lir_xchg, src, set, res, tmp)); } diff --git a/src/hotspot/share/c1/c1_LIRAssembler.hpp b/src/hotspot/share/c1/c1_LIRAssembler.hpp index 4aaa45eca36..aded7a06eda 100644 --- a/src/hotspot/share/c1/c1_LIRAssembler.hpp +++ b/src/hotspot/share/c1/c1_LIRAssembler.hpp @@ -210,6 +210,7 @@ class LIR_Assembler: public CompilationResourceObj { void emit_rtcall(LIR_OpRTCall* op); void emit_profile_call(LIR_OpProfileCall* op); void emit_profile_type(LIR_OpProfileType* op); + void emit_profile_inline_type(LIR_OpProfileInlineType* op); void emit_delay(LIR_OpDelay* op); void emit_std_entries(); void emit_std_entry(CodeOffsets::Entries entry, const CompiledEntrySignature* ces); diff --git a/src/hotspot/share/c1/c1_LIRGenerator.cpp b/src/hotspot/share/c1/c1_LIRGenerator.cpp index 70e68856696..97d492841b1 100644 --- a/src/hotspot/share/c1/c1_LIRGenerator.cpp +++ b/src/hotspot/share/c1/c1_LIRGenerator.cpp @@ -1809,7 +1809,7 @@ void LIRGenerator::do_StoreIndexed(StoreIndexed* x) { if (is_loaded_flattened_array) { int flag = ArrayLoadStoreData::flat_array_byte_constant() | ArrayLoadStoreData::null_free_array_byte_constant(); assert(md != NULL, "should have been initialized"); - profile_array_load_store_flags(md, load_store, flag); + profile_flags(md, load_store, flag); } else if (x->array()->maybe_null_free_array()) { profile_null_free_array(array, md, load_store); } @@ -2193,7 +2193,7 @@ void LIRGenerator::do_LoadIndexed(LoadIndexed* x) { if (x->should_profile()) { fatal("Loaded flattened array should not be profiled"); int flag = ArrayLoadStoreData::flat_array_byte_constant() | ArrayLoadStoreData::null_free_array_byte_constant(); - profile_array_load_store_flags(md, load_store, flag); + profile_flags(md, load_store, flag); } } else if (x->delayed() != NULL) { assert(x->array()->is_loaded_flattened_array(), "must be"); @@ -3015,31 +3015,28 @@ void LIRGenerator::profile_parameters(Base* x) { } } -void LIRGenerator::profile_array_load_store_flags(ciMethodData* md, ciArrayLoadStoreData* load_store, int flag, LIR_Opr mdp) { - assert(md != NULL && load_store != NULL, "should have been initialized"); - if (mdp == NULL) { - mdp = new_register(T_METADATA); - __ metadata2reg(md->constant_encoding(), mdp); +void LIRGenerator::profile_flags(ciMethodData* md, ciProfileData* data, int flag, LIR_Condition condition) { + assert(md != NULL && data != NULL, "should have been initialized"); + LIR_Opr mdp = new_register(T_METADATA); + __ metadata2reg(md->constant_encoding(), mdp); + LIR_Address* addr = new LIR_Address(mdp, md->byte_offset_of_slot(data, DataLayout::flags_offset()), T_BYTE); + LIR_Opr flags = new_register(T_INT); + __ move(addr, flags); + if (condition != lir_cond_always) { + LIR_Opr update = new_register(T_INT); + __ cmove(condition, LIR_OprFact::intConst(0), LIR_OprFact::intConst(flag), update, T_INT); + } else { + __ logical_or(flags, LIR_OprFact::intConst(flag), flags); } - LIR_Address* addr = new LIR_Address(mdp, md->byte_offset_of_slot(load_store, DataLayout::flags_offset()), T_BYTE); - LIR_Opr id = new_register(T_INT); - __ move(addr, id); - __ logical_or(id, LIR_OprFact::intConst(flag), id); - __ store(id, addr); + __ store(flags, addr); } void LIRGenerator::profile_null_free_array(LIRItem array, ciMethodData* md, ciArrayLoadStoreData* load_store) { LabelObj* L_end = new LabelObj(); LIR_Opr tmp = new_register(T_METADATA); - LIR_Opr mdp = new_register(T_METADATA); - assert(md != NULL, "should have been initialized"); - __ metadata2reg(md->constant_encoding(), mdp); __ check_null_free_array(array.result(), tmp); - __ branch(lir_cond_equal, L_end->label()); - - profile_array_load_store_flags(md, load_store, ArrayLoadStoreData::null_free_array_byte_constant(), mdp); - __ branch_destination(L_end->label()); + profile_flags(md, load_store, ArrayLoadStoreData::null_free_array_byte_constant(), lir_cond_equal); } void LIRGenerator::profile_array_type(AccessIndexed* x, ciMethodData*& md, ciArrayLoadStoreData*& load_store) { @@ -3779,6 +3776,53 @@ void LIRGenerator::do_ProfileReturnType(ProfileReturnType* x) { } } +bool LIRGenerator::profile_inline_klass(ciMethodData* md, ciProfileData* data, Value value, int flag) { + ciKlass* klass = value->as_loaded_klass_or_null(); + if (klass != NULL) { + if (klass->is_inlinetype()) { + profile_flags(md, data, flag, lir_cond_always); + } else if (klass->can_be_inline_klass()) { + return false; + } + } else { + return false; + } + return true; +} + + +void LIRGenerator::do_ProfileACmpTypes(ProfileACmpTypes* x) { + ciMethod* method = x->method(); + assert(method != NULL, "method should be set if branch is profiled"); + ciMethodData* md = method->method_data_or_null(); + assert(md != NULL, "Sanity"); + ciProfileData* data = md->bci_to_data(x->bci()); + assert(data != NULL, "must have profiling data"); + assert(data->is_ACmpData(), "need BranchData for two-way branches"); + ciACmpData* acmp = (ciACmpData*)data; + LIR_Opr mdp = LIR_OprFact::illegalOpr; + profile_type(md, md->byte_offset_of_slot(acmp, ACmpData::left_offset()), 0, + acmp->left()->type(), x->left(), mdp, !x->left_maybe_null(), NULL, NULL); + int flags_offset = md->byte_offset_of_slot(data, DataLayout::flags_offset()); + if (!profile_inline_klass(md, acmp, x->left(), ACmpData::left_inline_type_byte_constant())) { + LIR_Opr mdp = new_register(T_METADATA); + __ metadata2reg(md->constant_encoding(), mdp); + LIRItem value(x->left(), this); + value.load_item(); + __ profile_inline_type(new LIR_Address(mdp, flags_offset, T_INT), value.result(), ACmpData::left_inline_type_byte_constant(), new_register(T_INT), !x->left_maybe_null()); + } + profile_type(md, md->byte_offset_of_slot(acmp, ACmpData::left_offset()), + in_bytes(ACmpData::right_offset()) - in_bytes(ACmpData::left_offset()), + acmp->right()->type(), x->right(), mdp, !x->right_maybe_null(), NULL, NULL); + if (!profile_inline_klass(md, acmp, x->right(), ACmpData::right_inline_type_byte_constant())) { + LIR_Opr mdp = new_register(T_METADATA); + __ metadata2reg(md->constant_encoding(), mdp); + LIRItem value(x->right(), this); + value.load_item(); + __ profile_inline_type(new LIR_Address(mdp, flags_offset, T_INT), value.result(), ACmpData::right_inline_type_byte_constant(), new_register(T_INT), !x->left_maybe_null()); + } +} + void LIRGenerator::do_ProfileInvoke(ProfileInvoke* x) { // We can safely ignore accessors here, since c2 will inline them anyway, // accessors are also always mature. diff --git a/src/hotspot/share/c1/c1_LIRGenerator.hpp b/src/hotspot/share/c1/c1_LIRGenerator.hpp index bc74f43fcf4..a572a517fe2 100644 --- a/src/hotspot/share/c1/c1_LIRGenerator.hpp +++ b/src/hotspot/share/c1/c1_LIRGenerator.hpp @@ -500,10 +500,11 @@ class LIRGenerator: public InstructionVisitor, public BlockClosure { void profile_arguments(ProfileCall* x); void profile_parameters(Base* x); void profile_parameters_at_call(ProfileCall* x); - void profile_array_load_store_flags(ciMethodData* md, ciArrayLoadStoreData* load_store, int flag, LIR_Opr mdp = NULL); + void profile_flags(ciMethodData* md, ciProfileData* load_store, int flag, LIR_Condition condition = lir_cond_always); void profile_null_free_array(LIRItem array, ciMethodData* md, ciArrayLoadStoreData* load_store); void profile_array_type(AccessIndexed* x, ciMethodData*& md, ciArrayLoadStoreData*& load_store); void profile_element_type(Value element, ciMethodData* md, ciArrayLoadStoreData* load_store); + bool profile_inline_klass(ciMethodData* md, ciProfileData* data, Value value, int flag); LIR_Opr mask_boolean(LIR_Opr array, LIR_Opr value, CodeEmitInfo*& null_check_info); LIR_Opr maybe_mask_boolean(StoreIndexed* x, LIR_Opr array, LIR_Opr value, CodeEmitInfo*& null_check_info); @@ -617,6 +618,7 @@ class LIRGenerator: public InstructionVisitor, public BlockClosure { virtual void do_ProfileCall (ProfileCall* x); virtual void do_ProfileReturnType (ProfileReturnType* x); virtual void do_ProfileInvoke (ProfileInvoke* x); + virtual void do_ProfileACmpTypes(ProfileACmpTypes* x); virtual void do_RuntimeCall (RuntimeCall* x); virtual void do_MemBar (MemBar* x); virtual void do_RangeCheckPredicate(RangeCheckPredicate* x); diff --git a/src/hotspot/share/c1/c1_Optimizer.cpp b/src/hotspot/share/c1/c1_Optimizer.cpp index 69977b65cd6..7d0355a7b12 100644 --- a/src/hotspot/share/c1/c1_Optimizer.cpp +++ b/src/hotspot/share/c1/c1_Optimizer.cpp @@ -543,6 +543,7 @@ class NullCheckVisitor: public InstructionVisitor { void do_UnsafeGetAndSetObject(UnsafeGetAndSetObject* x); void do_ProfileCall (ProfileCall* x); void do_ProfileReturnType (ProfileReturnType* x); + void do_ProfileACmpTypes(ProfileACmpTypes* x); void do_ProfileInvoke (ProfileInvoke* x); void do_RuntimeCall (RuntimeCall* x); void do_MemBar (MemBar* x); @@ -672,6 +673,7 @@ class NullCheckEliminator: public ValueVisitor { void handle_Phi (Phi* x); void handle_ProfileCall (ProfileCall* x); void handle_ProfileReturnType (ProfileReturnType* x); + void handle_ProfileACmpTypes(ProfileACmpTypes* x); }; @@ -735,6 +737,7 @@ void NullCheckVisitor::do_ProfileCall (ProfileCall* x) { nce()->clear_las nce()->handle_ProfileCall(x); } void NullCheckVisitor::do_ProfileReturnType (ProfileReturnType* x) { nce()->handle_ProfileReturnType(x); } void NullCheckVisitor::do_ProfileInvoke (ProfileInvoke* x) {} +void NullCheckVisitor::do_ProfileACmpTypes(ProfileACmpTypes* x) { nce()->handle_ProfileACmpTypes(x); } void NullCheckVisitor::do_RuntimeCall (RuntimeCall* x) {} void NullCheckVisitor::do_MemBar (MemBar* x) {} void NullCheckVisitor::do_RangeCheckPredicate(RangeCheckPredicate* x) {} @@ -1169,6 +1172,11 @@ void NullCheckEliminator::handle_ProfileReturnType(ProfileReturnType* x) { x->set_needs_null_check(!set_contains(x->ret())); } +void NullCheckEliminator::handle_ProfileACmpTypes(ProfileACmpTypes* x) { + x->set_left_maybe_null(!set_contains(x->left())); + x->set_right_maybe_null(!set_contains(x->right())); +} + void Optimizer::eliminate_null_checks() { ResourceMark rm; diff --git a/src/hotspot/share/c1/c1_RangeCheckElimination.hpp b/src/hotspot/share/c1/c1_RangeCheckElimination.hpp index 5564adee55f..af2647f36b5 100644 --- a/src/hotspot/share/c1/c1_RangeCheckElimination.hpp +++ b/src/hotspot/share/c1/c1_RangeCheckElimination.hpp @@ -172,6 +172,7 @@ class RangeCheckEliminator { void do_UnsafeGetAndSetObject(UnsafeGetAndSetObject* x) { /* nothing to do */ }; void do_ProfileCall (ProfileCall* x) { /* nothing to do */ }; void do_ProfileReturnType (ProfileReturnType* x) { /* nothing to do */ }; + void do_ProfileACmpTypes(ProfileACmpTypes* x) { /* nothing to do */ }; void do_ProfileInvoke (ProfileInvoke* x) { /* nothing to do */ }; void do_RuntimeCall (RuntimeCall* x) { /* nothing to do */ }; void do_MemBar (MemBar* x) { /* nothing to do */ }; diff --git a/src/hotspot/share/c1/c1_ValueMap.hpp b/src/hotspot/share/c1/c1_ValueMap.hpp index fb1c537fbb2..5144204dd06 100644 --- a/src/hotspot/share/c1/c1_ValueMap.hpp +++ b/src/hotspot/share/c1/c1_ValueMap.hpp @@ -208,6 +208,7 @@ class ValueNumberingVisitor: public InstructionVisitor { void do_RoundFP (RoundFP* x) { /* nothing to do */ } void do_ProfileCall (ProfileCall* x) { /* nothing to do */ } void do_ProfileReturnType (ProfileReturnType* x) { /* nothing to do */ } + void do_ProfileACmpTypes(ProfileACmpTypes* x) { /* nothing to do */ } void do_ProfileInvoke (ProfileInvoke* x) { /* nothing to do */ }; void do_RuntimeCall (RuntimeCall* x) { /* nothing to do */ }; void do_MemBar (MemBar* x) { /* nothing to do */ }; diff --git a/src/hotspot/share/ci/ciMethod.cpp b/src/hotspot/share/ci/ciMethod.cpp index cb8475f06e5..9afd0f478aa 100644 --- a/src/hotspot/share/ci/ciMethod.cpp +++ b/src/hotspot/share/ci/ciMethod.cpp @@ -706,6 +706,23 @@ bool ciMethod::array_access_profiled_type(int bci, ciKlass*& array_type, ciKlass return false; } +bool ciMethod::acmp_profiled_type(int bci, ciKlass*& left_type, ciKlass*& right_type, ProfilePtrKind& left_ptr, ProfilePtrKind& right_ptr, bool &left_inline_type, bool &right_inline_type) { + if (method_data() != NULL && method_data()->is_mature()) { + ciProfileData* data = method_data()->bci_to_data(bci); + if (data != NULL && data->is_ACmpData()) { + ciACmpData* acmp = (ciACmpData*)data->as_ACmpData(); + left_type = acmp->left()->valid_type(); + right_type = acmp->right()->valid_type(); + left_ptr = acmp->left()->ptr_kind(); + right_ptr = acmp->right()->ptr_kind(); + left_inline_type = acmp->left_inline_type(); + right_inline_type = acmp->right_inline_type(); + return true; + } + } + return false; +} + // ------------------------------------------------------------------ // ciMethod::find_monomorphic_target diff --git a/src/hotspot/share/ci/ciMethod.hpp b/src/hotspot/share/ci/ciMethod.hpp index c5726aedc99..17f0387b1a8 100644 --- a/src/hotspot/share/ci/ciMethod.hpp +++ b/src/hotspot/share/ci/ciMethod.hpp @@ -43,6 +43,7 @@ class xmlStream; // Whether profiling found an oop to be always, never or sometimes // null enum ProfilePtrKind { + ProfileUnknownNull, ProfileAlwaysNull, ProfileNeverNull, ProfileMaybeNull @@ -265,7 +266,9 @@ class ciMethod : public ciMetadata { bool parameter_profiled_type(int i, ciKlass*& type, ProfilePtrKind& ptr_kind); bool return_profiled_type(int bci, ciKlass*& type, ProfilePtrKind& ptr_kind); bool array_access_profiled_type(int bci, ciKlass*& array_type, ciKlass*& element_type, ProfilePtrKind& element_ptr, bool &flat_array, bool &null_free); - + bool acmp_profiled_type(int bci, ciKlass*& left_type, ciKlass*& right_type, + ProfilePtrKind& left_ptr, ProfilePtrKind& right_ptr, + bool &left_inline_type, bool &right_inline_type); ciField* get_field_at_bci( int bci, bool &will_link); ciMethod* get_method_at_bci(int bci, bool &will_link, ciSignature* *declared_signature); ciMethod* get_method_at_bci(int bci) { diff --git a/src/hotspot/share/ci/ciMethodData.cpp b/src/hotspot/share/ci/ciMethodData.cpp index ddfc5780b9c..6e6abb9457f 100644 --- a/src/hotspot/share/ci/ciMethodData.cpp +++ b/src/hotspot/share/ci/ciMethodData.cpp @@ -367,6 +367,8 @@ ciProfileData* ciMethodData::data_at(int data_index) { return new ciParametersTypeData(data_layout); case DataLayout::array_load_store_data_tag: return new ciArrayLoadStoreData(data_layout); + case DataLayout::acmp_data_tag: + return new ciACmpData(data_layout); }; } @@ -916,4 +918,15 @@ void ciArrayLoadStoreData::print_data_on(outputStream* st, const char* extra) co st->print("element"); element()->print_data_on(st); } + +void ciACmpData::print_data_on(outputStream* st, const char* extra) const { + BranchData::print_data_on(st, extra); + st->cr(); + tab(st, true); + st->print("left"); + left()->print_data_on(st); + tab(st, true); + st->print("right"); + right()->print_data_on(st); +} #endif diff --git a/src/hotspot/share/ci/ciMethodData.hpp b/src/hotspot/share/ci/ciMethodData.hpp index 89e7999cfd7..ad6f5bf96ec 100644 --- a/src/hotspot/share/ci/ciMethodData.hpp +++ b/src/hotspot/share/ci/ciMethodData.hpp @@ -379,6 +379,23 @@ class ciArrayLoadStoreData : public ArrayLoadStoreData { #endif }; +class ciACmpData : public ACmpData { +public: + ciACmpData(DataLayout* layout) : ACmpData(layout) {} + + ciSingleTypeEntry* left() const { return (ciSingleTypeEntry*)ACmpData::left(); } + ciSingleTypeEntry* right() const { return (ciSingleTypeEntry*)ACmpData::right(); } + + virtual void translate_from(const ProfileData* data) { + left()->translate_type_data_from(data->as_ACmpData()->left()); + right()->translate_type_data_from(data->as_ACmpData()->right()); + } + +#ifndef PRODUCT + void print_data_on(outputStream* st, const char* extra = NULL) const; +#endif +}; + // ciMethodData // // This class represents a MethodData* in the HotSpot virtual diff --git a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp index 7d1ef915b70..c78f7a1c4b0 100644 --- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp +++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp @@ -513,6 +513,7 @@ declare_constant(DataLayout::parameters_type_data_tag) \ declare_constant(DataLayout::speculative_trap_data_tag) \ declare_constant(DataLayout::array_load_store_data_tag) \ + declare_constant(DataLayout::acmp_data_tag) \ \ declare_constant(Deoptimization::Unpack_deopt) \ declare_constant(Deoptimization::Unpack_exception) \ diff --git a/src/hotspot/share/oops/methodData.cpp b/src/hotspot/share/oops/methodData.cpp index 3a94f05c9d6..ce6113bd3a1 100644 --- a/src/hotspot/share/oops/methodData.cpp +++ b/src/hotspot/share/oops/methodData.cpp @@ -523,6 +523,10 @@ void BranchData::post_initialize(BytecodeStream* stream, MethodData* mdo) { void BranchData::print_data_on(outputStream* st, const char* extra) const { print_shared(st, "BranchData", extra); + if (data()->flags()) { + tty->cr(); + tab(st); + } st->print_cr("taken(%u) displacement(%d)", taken(), displacement()); tab(st); @@ -657,6 +661,16 @@ void ArrayLoadStoreData::print_data_on(outputStream* st, const char* extra) cons _element.print_data_on(st); } +void ACmpData::print_data_on(outputStream* st, const char* extra) const { + BranchData::print_data_on(st, extra); + tab(st, true); + st->print("left"); + _left.print_data_on(st); + tab(st, true); + st->print("right"); + _right.print_data_on(st); +} + // ================================================================== // MethodData* // @@ -724,11 +738,12 @@ int MethodData::bytecode_cell_count(Bytecodes::Code code) { case Bytecodes::_if_icmpge: case Bytecodes::_if_icmpgt: case Bytecodes::_if_icmple: - case Bytecodes::_if_acmpeq: - case Bytecodes::_if_acmpne: case Bytecodes::_ifnull: case Bytecodes::_ifnonnull: return BranchData::static_cell_count(); + case Bytecodes::_if_acmpne: + case Bytecodes::_if_acmpeq: + return ACmpData::static_cell_count(); case Bytecodes::_lookupswitch: case Bytecodes::_tableswitch: return variable_cell_count; @@ -1075,13 +1090,16 @@ int MethodData::initialize_data(BytecodeStream* stream, case Bytecodes::_if_icmpge: case Bytecodes::_if_icmpgt: case Bytecodes::_if_icmple: - case Bytecodes::_if_acmpeq: - case Bytecodes::_if_acmpne: case Bytecodes::_ifnull: case Bytecodes::_ifnonnull: cell_count = BranchData::static_cell_count(); tag = DataLayout::branch_data_tag; break; + case Bytecodes::_if_acmpeq: + case Bytecodes::_if_acmpne: + cell_count = ACmpData::static_cell_count(); + tag = DataLayout::acmp_data_tag; + break; case Bytecodes::_lookupswitch: case Bytecodes::_tableswitch: cell_count = MultiBranchData::compute_cell_count(stream); @@ -1151,6 +1169,8 @@ ProfileData* DataLayout::data_in() { return new SpeculativeTrapData(this); case DataLayout::array_load_store_data_tag: return new ArrayLoadStoreData(this); + case DataLayout::acmp_data_tag: + return new ACmpData(this); } } diff --git a/src/hotspot/share/oops/methodData.hpp b/src/hotspot/share/oops/methodData.hpp index db25b6fe557..0f0d97927cc 100644 --- a/src/hotspot/share/oops/methodData.hpp +++ b/src/hotspot/share/oops/methodData.hpp @@ -124,7 +124,8 @@ class DataLayout { virtual_call_type_data_tag, parameters_type_data_tag, speculative_trap_data_tag, - array_load_store_data_tag + array_load_store_data_tag, + acmp_data_tag }; enum { @@ -257,6 +258,7 @@ class RetData; class CallTypeData; class JumpData; class BranchData; +class ACmpData; class ArrayData; class MultiBranchData; class ArgInfoData; @@ -392,6 +394,7 @@ class ProfileData : public ResourceObj { virtual bool is_ParametersTypeData() const { return false; } virtual bool is_SpeculativeTrapData()const { return false; } virtual bool is_ArrayLoadStoreData() const { return false; } + virtual bool is_ACmpData() const { return false; } BitData* as_BitData() const { @@ -454,6 +457,10 @@ class ProfileData : public ResourceObj { assert(is_ArrayLoadStoreData(), "wrong type"); return is_ArrayLoadStoreData() ? (ArrayLoadStoreData*)this : NULL; } + ACmpData* as_ACmpData() const { + assert(is_ACmpData(), "wrong type"); + return is_ACmpData() ? (ACmpData*)this : NULL; + } // Subclass specific initialization @@ -612,7 +619,8 @@ class JumpData : public ProfileData { public: JumpData(DataLayout* layout) : ProfileData(layout) { assert(layout->tag() == DataLayout::jump_data_tag || - layout->tag() == DataLayout::branch_data_tag, "wrong type"); + layout->tag() == DataLayout::branch_data_tag || + layout->tag() == DataLayout::acmp_data_tag, "wrong type"); } virtual bool is_JumpData() const { return true; } @@ -1496,7 +1504,7 @@ class BranchData : public JumpData { public: BranchData(DataLayout* layout) : JumpData(layout) { - assert(layout->tag() == DataLayout::branch_data_tag, "wrong type"); + assert(layout->tag() == DataLayout::branch_data_tag || layout->tag() == DataLayout::acmp_data_tag, "wrong type"); } virtual bool is_BranchData() const { return true; } @@ -1926,6 +1934,79 @@ class ArrayLoadStoreData : public ProfileData { virtual void print_data_on(outputStream* st, const char* extra = NULL) const; }; +class ACmpData : public BranchData { +private: + enum { + left_inline_type_flag = DataLayout::first_flag, + right_inline_type_flag + }; + + SingleTypeEntry _left; + SingleTypeEntry _right; + +public: + ACmpData(DataLayout* layout) : + BranchData(layout), + _left(BranchData::static_cell_count()), + _right(BranchData::static_cell_count() + SingleTypeEntry::static_cell_count()) { + assert(layout->tag() == DataLayout::acmp_data_tag, "wrong type"); + _left.set_profile_data(this); + _right.set_profile_data(this); + } + + const SingleTypeEntry* left() const { + return &_left; + } + + const SingleTypeEntry* right() const { + return &_right; + } + + virtual bool is_ACmpData() const { return true; } + + static int static_cell_count() { + return BranchData::static_cell_count()+ SingleTypeEntry::static_cell_count() * 2; + } + + virtual int cell_count() const { + return static_cell_count(); + } + + void set_left_inline_type() { set_flag_at(left_inline_type_flag); } + bool left_inline_type() const { return flag_at(left_inline_type_flag); } + + void set_right_inline_type() { set_flag_at(right_inline_type_flag); } + bool right_inline_type() const { return flag_at(right_inline_type_flag); } + + // Code generation support + static int left_inline_type_byte_constant() { + return flag_number_to_constant(left_inline_type_flag); + } + + static int right_inline_type_byte_constant() { + return flag_number_to_constant(right_inline_type_flag); + } + + static ByteSize left_offset() { + return cell_offset(BranchData::static_cell_count()); + } + + static ByteSize right_offset() { + return cell_offset(BranchData::static_cell_count() + SingleTypeEntry::static_cell_count()); + } + + virtual void clean_weak_klass_links(bool always_clean) { + _left.clean_weak_klass_links(always_clean); + _right.clean_weak_klass_links(always_clean); + } + + static ByteSize acmp_data_size() { + return cell_offset(static_cell_count()); + } + + virtual void print_data_on(outputStream* st, const char* extra = NULL) const; +}; + // MethodData* // // A MethodData* holds information which has been collected about diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index 850fd4f62df..c4f43f0d236 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -780,6 +780,9 @@ product(bool, UseArrayLoadStoreProfile, true, \ "Take advantage of profiling at array load/store") \ \ + product(bool, UseACmpProfile, true, \ + "Take advantage of profiling at acmp") \ + \ product(bool, ExpandSubTypeCheckAtParseTime, false, DIAGNOSTIC, \ "Do not use subtype check macro node") diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index ac43af09d39..abc45391fc3 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -2268,7 +2268,7 @@ Node* GraphKit::record_profile_for_speculation(Node* n, ciKlass* exact_kls, Prof assert(xtype->klass_is_exact(), "Should be exact"); // Any reason to believe n is not null (from this profiling or a previous one)? assert(ptr_kind != ProfileAlwaysNull, "impossible here"); - const TypePtr* ptr = (ptr_kind == ProfileMaybeNull && current_type->speculative_maybe_null()) ? TypePtr::BOTTOM : TypePtr::NOTNULL; + const TypePtr* ptr = (ptr_kind != ProfileNeverNull && current_type->speculative_maybe_null()) ? TypePtr::BOTTOM : TypePtr::NOTNULL; // record the new speculative type's depth speculative = xtype->cast_to_ptr_type(ptr->ptr())->is_ptr(); speculative = speculative->with_inline_depth(jvms()->depth()); @@ -3548,13 +3548,20 @@ Node* GraphKit::gen_checkcast(Node *obj, Node* superklass, Node* *failure_contro } // Check if 'obj' is an inline type by checking if it has the always_locked markWord pattern set. -Node* GraphKit::is_inline_type(Node* obj) { +Node* GraphKit::inline_type_test(Node* obj) { Node* mark_addr = basic_plus_adr(obj, oopDesc::mark_offset_in_bytes()); Node* mark = make_load(NULL, mark_addr, TypeX_X, TypeX_X->basic_type(), MemNode::unordered); Node* mask = _gvn.MakeConX(markWord::always_locked_pattern); Node* andx = _gvn.transform(new AndXNode(mark, mask)); - Node* cmp = _gvn.transform(new CmpXNode(andx, mask)); - return _gvn.transform(new BoolNode(cmp, BoolTest::eq)); + return _gvn.transform(new CmpXNode(andx, mask)); +} + +Node* GraphKit::is_inline_type(Node* obj) { + return _gvn.transform(new BoolNode(inline_type_test(obj), BoolTest::eq)); +} + +Node* GraphKit::is_not_inline_type(Node* obj) { + return _gvn.transform(new BoolNode(inline_type_test(obj), BoolTest::ne)); } // Check if 'ary' is a non-flattened array diff --git a/src/hotspot/share/opto/graphKit.hpp b/src/hotspot/share/opto/graphKit.hpp index c3c22f940fe..735eaae6a23 100644 --- a/src/hotspot/share/opto/graphKit.hpp +++ b/src/hotspot/share/opto/graphKit.hpp @@ -853,7 +853,9 @@ class GraphKit : public Phase { // and the array-store bytecode Node* gen_checkcast(Node *subobj, Node* superkls, Node* *failure_control = NULL); + Node* inline_type_test(Node* obj); Node* is_inline_type(Node* obj); + Node* is_not_inline_type(Node* obj); Node* is_non_flattened_array(Node* ary); Node* check_null_free_bit(Node* klass, bool null_free); Node* is_nullable_array(Node* ary); diff --git a/src/hotspot/share/opto/parse.hpp b/src/hotspot/share/opto/parse.hpp index 5441e39faa6..b49c78af93b 100644 --- a/src/hotspot/share/opto/parse.hpp +++ b/src/hotspot/share/opto/parse.hpp @@ -549,7 +549,11 @@ class Parse : public GraphKit { void do_ifnull(BoolTest::mask btest, Node* c); void do_if(BoolTest::mask btest, Node* c, bool new_path = false, Node** ctrl_taken = NULL); - void do_acmp(BoolTest::mask btest, Node* a, Node* b); + void do_acmp(BoolTest::mask btest, Node* left, Node* right); + void acmp_always_null_input(Node* input, const TypeOopPtr* tinput, BoolTest::mask btest, Node* eq_region); + void acmp_known_non_inline_type_input(Node* input, const TypeOopPtr* tinput, ProfilePtrKind input_ptr, ciKlass* input_type, BoolTest::mask btest, Node* eq_region); + Node* acmp_null_check(Node* input, const TypeOopPtr* tinput, ProfilePtrKind input_ptr, Node*& null_ctl); + void acmp_unknown_non_inline_type_input(Node* input, const TypeOopPtr* tinput, ProfilePtrKind input_ptr, BoolTest::mask btest, Node* eq_region); int repush_if_args(); void adjust_map_after_if(BoolTest::mask btest, Node* c, float prob, Block* path); void sharpen_type_after_if(BoolTest::mask btest, diff --git a/src/hotspot/share/opto/parse2.cpp b/src/hotspot/share/opto/parse2.cpp index 4e489de7b4f..5cdd2cc2dab 100644 --- a/src/hotspot/share/opto/parse2.cpp +++ b/src/hotspot/share/opto/parse2.cpp @@ -2053,35 +2053,186 @@ void Parse::do_if(BoolTest::mask btest, Node* c, bool new_path, Node** ctrl_take } } -void Parse::do_acmp(BoolTest::mask btest, Node* a, Node* b) { + +static ProfilePtrKind speculative_ptr_kind(const TypeOopPtr* t) { + if (t->speculative() == NULL) { + return ProfileUnknownNull; + } + if (t->speculative_always_null()) { + return ProfileAlwaysNull; + } + if (t->speculative_maybe_null()) { + return ProfileMaybeNull; + } + return ProfileNeverNull; +} + +void Parse::acmp_always_null_input(Node* input, const TypeOopPtr* tinput, BoolTest::mask btest, Node* eq_region) { + inc_sp(2); + Node* cast = null_check_common(input, T_OBJECT, true, NULL, + !too_many_traps_or_recompiles(Deoptimization::Reason_speculate_null_check) && + speculative_ptr_kind(tinput) == ProfileAlwaysNull); + dec_sp(2); + if (btest == BoolTest::ne) { + { + PreserveJVMState pjvms(this); + replace_in_map(input, cast); + int target_bci = iter().get_dest(); + merge(target_bci); + } + record_for_igvn(eq_region); + set_control(_gvn.transform(eq_region)); + } else { + replace_in_map(input, cast); + } +} + +Node* Parse::acmp_null_check(Node* input, const TypeOopPtr* tinput, ProfilePtrKind input_ptr, Node*& null_ctl) { + inc_sp(2); + null_ctl = top(); + Node* cast = null_check_oop(input, &null_ctl, + input_ptr == ProfileNeverNull || (input_ptr == ProfileUnknownNull && !too_many_traps_or_recompiles(Deoptimization::Reason_null_check)), + false, + speculative_ptr_kind(tinput) == ProfileNeverNull && + !too_many_traps_or_recompiles(Deoptimization::Reason_speculate_null_check)); + dec_sp(2); + assert(!stopped(), "null input should have been caught earlier"); + return cast; +} + +void Parse::acmp_known_non_inline_type_input(Node* input, const TypeOopPtr* tinput, ProfilePtrKind input_ptr, ciKlass* input_type, BoolTest::mask btest, Node* eq_region) { + Node* ne_region = new RegionNode(1); + Node* null_ctl; + Node* cast = acmp_null_check(input, tinput, input_ptr, null_ctl); + ne_region->add_req(null_ctl); + + Node* slow_ctl = type_check_receiver(cast, input_type, 1.0, &cast); + { + PreserveJVMState pjvms(this); + inc_sp(2); + set_control(slow_ctl); + Deoptimization::DeoptReason reason; + if (tinput->speculative_type() != NULL && !too_many_traps_or_recompiles(Deoptimization::Reason_speculate_class_check)) { + reason = Deoptimization::Reason_speculate_class_check; + } else { + reason = Deoptimization::Reason_class_check; + } + uncommon_trap_exact(reason, Deoptimization::Action_maybe_recompile); + } + ne_region->add_req(control()); + + record_for_igvn(ne_region); + set_control(_gvn.transform(ne_region)); + if (btest == BoolTest::ne) { + { + PreserveJVMState pjvms(this); + if (null_ctl == top()) { + replace_in_map(input, cast); + } + int target_bci = iter().get_dest(); + merge(target_bci); + } + record_for_igvn(eq_region); + set_control(_gvn.transform(eq_region)); + } else { + if (null_ctl == top()) { + replace_in_map(input, cast); + } + set_control(_gvn.transform(ne_region)); + } +} + +void Parse::acmp_unknown_non_inline_type_input(Node* input, const TypeOopPtr* tinput, ProfilePtrKind input_ptr, BoolTest::mask btest, Node* eq_region) { + Node* ne_region = new RegionNode(1); + Node* null_ctl; + Node* cast = acmp_null_check(input, tinput, input_ptr, null_ctl); + ne_region->add_req(null_ctl); + + { + BuildCutout unless(this, is_not_inline_type(cast), PROB_MAX); + inc_sp(2); + uncommon_trap_exact(Deoptimization::Reason_class_check, Deoptimization::Action_maybe_recompile); + } + + ne_region->add_req(control()); + + record_for_igvn(ne_region); + set_control(_gvn.transform(ne_region)); + if (btest == BoolTest::ne) { + { + PreserveJVMState pjvms(this); + if (null_ctl == top()) { + replace_in_map(input, cast); + } + int target_bci = iter().get_dest(); + merge(target_bci); + } + record_for_igvn(eq_region); + set_control(_gvn.transform(eq_region)); + } else { + if (null_ctl == top()) { + replace_in_map(input, cast); + } + set_control(_gvn.transform(ne_region)); + } +} + +void Parse::do_acmp(BoolTest::mask btest, Node* left, Node* right) { + ciKlass* left_type = NULL; + ciKlass* right_type = NULL; + ProfilePtrKind left_ptr = ProfileUnknownNull; + ProfilePtrKind right_ptr = ProfileUnknownNull; + bool left_inline_type = true; + bool right_inline_type = true; + + // Leverage profiling at acmp + if (UseACmpProfile) { + method()->acmp_profiled_type(bci(), left_type, right_type, left_ptr, right_ptr, left_inline_type, right_inline_type); + if (too_many_traps_or_recompiles(Deoptimization::Reason_class_check)) { + left_type = NULL; + right_type = NULL; + left_inline_type = true; + right_inline_type = true; + } + if (too_many_traps_or_recompiles(Deoptimization::Reason_null_check)) { + left_ptr = ProfileUnknownNull; + right_ptr = ProfileUnknownNull; + } + } + + if (UseTypeSpeculation) { + record_profile_for_speculation(left, left_type, left_ptr); + record_profile_for_speculation(right, right_type, right_ptr); + } + if (!EnableValhalla) { - Node* cmp = CmpP(a, b); + Node* cmp = CmpP(left, right); cmp = optimize_cmp_with_klass(cmp); do_if(btest, cmp); return; } // Allocate inline type operands and re-execute on deoptimization - if (a->is_InlineType()) { + if (left->is_InlineType()) { PreserveReexecuteState preexecs(this); inc_sp(2); jvms()->set_should_reexecute(true); - a = a->as_InlineType()->buffer(this)->get_oop(); + left = left->as_InlineType()->buffer(this)->get_oop(); } - if (b->is_InlineType()) { + if (right->is_InlineType()) { PreserveReexecuteState preexecs(this); inc_sp(2); jvms()->set_should_reexecute(true); - b = b->as_InlineType()->buffer(this)->get_oop(); + right = right->as_InlineType()->buffer(this)->get_oop(); } // First, do a normal pointer comparison - const TypeOopPtr* ta = _gvn.type(a)->isa_oopptr(); - const TypeOopPtr* tb = _gvn.type(b)->isa_oopptr(); - Node* cmp = CmpP(a, b); + const TypeOopPtr* tleft = _gvn.type(left)->isa_oopptr(); + const TypeOopPtr* tright = _gvn.type(right)->isa_oopptr(); + Node* cmp = CmpP(left, right); cmp = optimize_cmp_with_klass(cmp); - if (ta == NULL || !ta->can_be_inline_type() || - tb == NULL || !tb->can_be_inline_type()) { + if (tleft == NULL || !tleft->can_be_inline_type() || + tright == NULL || !tright->can_be_inline_type()) { // This is sufficient, if one of the operands can't be an inline type do_if(btest, cmp); return; @@ -2111,60 +2262,85 @@ void Parse::do_acmp(BoolTest::mask btest, Node* a, Node* b) { set_control(is_not_equal); } - // Pointers are not equal, check if first operand is non-null - Node* ne_region = new RegionNode(6); - inc_sp(2); - Node* null_ctl = top(); - Node* not_null_a = null_check_oop(a, &null_ctl, !too_many_traps(Deoptimization::Reason_null_check), false, false); - dec_sp(2); - ne_region->init_req(1, null_ctl); - if (stopped()) { - record_for_igvn(ne_region); - set_control(_gvn.transform(ne_region)); - if (btest == BoolTest::ne) { - { - PreserveJVMState pjvms(this); - int target_bci = iter().get_dest(); - merge(target_bci); - } - record_for_igvn(eq_region); - set_control(_gvn.transform(eq_region)); + // Prefer speculative types if available + if (!too_many_traps_or_recompiles(Deoptimization::Reason_speculate_class_check)) { + if (tleft->speculative_type() != NULL) { + left_type = tleft->speculative_type(); + } + if (tright->speculative_type() != NULL) { + right_type = tright->speculative_type(); + } + } + + if (speculative_ptr_kind(tleft) != ProfileMaybeNull && speculative_ptr_kind(tleft) != ProfileUnknownNull) { + ProfilePtrKind speculative_left_ptr = speculative_ptr_kind(tleft); + if (speculative_left_ptr == ProfileAlwaysNull && !too_many_traps_or_recompiles(Deoptimization::Reason_speculate_null_assert)) { + left_ptr = speculative_left_ptr; + } else if (speculative_left_ptr == ProfileNeverNull && !too_many_traps_or_recompiles(Deoptimization::Reason_speculate_null_check)) { + left_ptr = speculative_left_ptr; + } + } + if (speculative_ptr_kind(tright) != ProfileMaybeNull && speculative_ptr_kind(tright) != ProfileUnknownNull) { + ProfilePtrKind speculative_right_ptr = speculative_ptr_kind(tright); + if (speculative_right_ptr == ProfileAlwaysNull && !too_many_traps_or_recompiles(Deoptimization::Reason_speculate_null_assert)) { + right_ptr = speculative_right_ptr; + } else if (speculative_right_ptr == ProfileNeverNull && !too_many_traps_or_recompiles(Deoptimization::Reason_speculate_null_check)) { + right_ptr = speculative_right_ptr; } + } + + if (left_ptr == ProfileAlwaysNull) { + // Comparison with null. Assert the input is indeed null and we're done. + acmp_always_null_input(left, tleft, btest, eq_region); + return; + } + if (right_ptr == ProfileAlwaysNull) { + // Comparison with null. Assert the input is indeed null and we're done. + acmp_always_null_input(right, tright, btest, eq_region); + return; + } + if (left_type != NULL && !left_type->is_inlinetype()) { + // Comparison with an object of known type + acmp_known_non_inline_type_input(left, tleft, left_ptr, left_type, btest, eq_region); + return; + } + if (right_type != NULL && !right_type->is_inlinetype()) { + // Comparison with an object of known type + acmp_known_non_inline_type_input(right, tright, right_ptr, right_type, btest, eq_region); return; } + if (!left_inline_type) { + // Comparison with an object known not to be an inline type + acmp_unknown_non_inline_type_input(left, tleft, left_ptr, btest, eq_region); + return; + } + if (!right_inline_type) { + // Comparison with an object known not to be an inline type + acmp_unknown_non_inline_type_input(right, tright, right_ptr, btest, eq_region); + return; + } + + // Pointers are not equal, check if first operand is non-null + Node* ne_region = new RegionNode(6); + Node* null_ctl; + Node* not_null_right = acmp_null_check(right, tright, right_ptr, null_ctl); + ne_region->init_req(1, null_ctl); // First operand is non-null, check if it is an inline type - Node* is_value = is_inline_type(not_null_a); + Node* is_value = is_inline_type(not_null_right); IfNode* is_value_iff = create_and_map_if(control(), is_value, PROB_FAIR, COUNT_UNKNOWN); Node* not_value = _gvn.transform(new IfFalseNode(is_value_iff)); ne_region->init_req(2, not_value); set_control(_gvn.transform(new IfTrueNode(is_value_iff))); // The first operand is an inline type, check if the second operand is non-null - inc_sp(2); - null_ctl = top(); - Node* not_null_b = null_check_oop(b, &null_ctl, !too_many_traps(Deoptimization::Reason_null_check), false, false); - dec_sp(2); + Node* not_null_left = acmp_null_check(left, tleft, left_ptr, null_ctl); ne_region->init_req(3, null_ctl); - if (stopped()) { - record_for_igvn(ne_region); - set_control(_gvn.transform(ne_region)); - if (btest == BoolTest::ne) { - { - PreserveJVMState pjvms(this); - int target_bci = iter().get_dest(); - merge(target_bci); - } - record_for_igvn(eq_region); - set_control(_gvn.transform(eq_region)); - } - return; - } // Check if both operands are of the same class. - Node* kls_a = load_object_klass(not_null_a); - Node* kls_b = load_object_klass(not_null_b); - Node* kls_cmp = CmpP(kls_a, kls_b); + Node* kls_left = load_object_klass(not_null_left); + Node* kls_right = load_object_klass(not_null_right); + Node* kls_cmp = CmpP(kls_left, kls_right); Node* kls_bol = _gvn.transform(new BoolNode(kls_cmp, BoolTest::ne)); IfNode* kls_iff = create_and_map_if(control(), kls_bol, PROB_FAIR, COUNT_UNKNOWN); Node* kls_ne = _gvn.transform(new IfTrueNode(kls_iff)); @@ -2205,8 +2381,8 @@ void Parse::do_acmp(BoolTest::mask btest, Node* a, Node* b) { ciMethod* subst_method = ciEnv::current()->ValueBootstrapMethods_klass()->find_method(ciSymbol::isSubstitutable_name(), ciSymbol::object_object_boolean_signature()); CallStaticJavaNode *call = new CallStaticJavaNode(C, TypeFunc::make(subst_method), SharedRuntime::get_resolve_static_call_stub(), subst_method, bci()); call->set_override_symbolic_info(true); - call->init_req(TypeFunc::Parms, not_null_a); - call->init_req(TypeFunc::Parms+1, not_null_b); + call->init_req(TypeFunc::Parms, not_null_left); + call->init_req(TypeFunc::Parms+1, not_null_right); inc_sp(2); set_edges_for_java_call(call, false, false); Node* ret = set_results_for_java_call(call, false, true); @@ -3370,7 +3546,7 @@ void Parse::do_one_bytecode() { maybe_add_safepoint(iter().get_dest()); a = pop(); b = pop(); - do_acmp(btest, a, b); + do_acmp(btest, b, a); break; case Bytecodes::_ifeq: btest = BoolTest::eq; goto handle_ifxx; diff --git a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotMethodData.java b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotMethodData.java index 081b528be49..433091fc78e 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotMethodData.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotMethodData.java @@ -107,6 +107,7 @@ static final class VMState { new UnknownProfileData(this, config.dataLayoutParametersTypeDataTag), new UnknownProfileData(this, config.dataLayoutSpeculativeTrapDataTag), new UnknownProfileData(this, config.dataLayoutArrayLoadStoreDataTag), + new UnknownProfileData(this, config.dataLayoutACmpDataTag), }; // @formatter:on diff --git a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotMethodDataAccessor.java b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotMethodDataAccessor.java index 0d316a55286..e8e7053255d 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotMethodDataAccessor.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotMethodDataAccessor.java @@ -57,7 +57,7 @@ int getTag() { static int readTag(HotSpotVMConfig config, HotSpotMethodData data, int position) { final int tag = data.readUnsignedByte(position, config.dataLayoutTagOffset); - assert tag >= config.dataLayoutNoTag && tag <= config.dataLayoutArrayLoadStoreDataTag : "profile data tag out of bounds: " + tag; + assert tag >= config.dataLayoutNoTag && tag <= config.dataLayoutACmpDataTag : "profile data tag out of bounds: " + tag; return tag; } diff --git a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMConfig.java b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMConfig.java index 0aff9e740a3..54a760308aa 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMConfig.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMConfig.java @@ -312,6 +312,7 @@ final int baseVtableLength() { final int dataLayoutParametersTypeDataTag = getConstant("DataLayout::parameters_type_data_tag", Integer.class); final int dataLayoutSpeculativeTrapDataTag = getConstant("DataLayout::speculative_trap_data_tag", Integer.class); final int dataLayoutArrayLoadStoreDataTag = getConstant("DataLayout::array_load_store_data_tag", Integer.class); + final int dataLayoutACmpDataTag = getConstant("DataLayout::acmp_data_tag", Integer.class); final int bciProfileWidth = getFlag("BciProfileWidth", Integer.class); final int typeProfileWidth = getFlag("TypeProfileWidth", Integer.class); diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/InlineTypeTest.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/InlineTypeTest.java index 4fcf3533a86..62fe7f8e992 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/InlineTypeTest.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/InlineTypeTest.java @@ -180,6 +180,8 @@ public abstract class InlineTypeTest { protected static final int ArrayLoadStoreProfileOff = 0x2000; protected static final int TypeProfileOn = 0x4000; protected static final int TypeProfileOff = 0x8000; + protected static final int ACmpProfileOn = 0x10000; + protected static final int ACmpProfileOff = 0x20000; protected static final boolean InlineTypePassFieldsAsArgs = (Boolean)WHITE_BOX.getVMFlag("InlineTypePassFieldsAsArgs"); protected static final boolean InlineTypeArrayFlatten = (WHITE_BOX.getIntxVMFlag("FlatArrayElementMaxSize") == -1); protected static final boolean InlineTypeReturnedAsFields = (Boolean)WHITE_BOX.getVMFlag("InlineTypeReturnedAsFields"); @@ -189,6 +191,7 @@ public abstract class InlineTypeTest { protected static final boolean VerifyOops = (Boolean)WHITE_BOX.getVMFlag("VerifyOops"); protected static final boolean UseArrayLoadStoreProfile = (Boolean)WHITE_BOX.getVMFlag("UseArrayLoadStoreProfile"); protected static final long TypeProfileLevel = (Long)WHITE_BOX.getVMFlag("TypeProfileLevel"); + protected static final boolean UseACmpProfile = (Boolean)WHITE_BOX.getVMFlag("UseACmpProfile"); protected static final Hashtable tests = new Hashtable(); protected static final boolean USE_COMPILER = WHITE_BOX.getBooleanVMFlag("UseCompiler"); @@ -224,6 +227,7 @@ public abstract class InlineTypeTest { protected static final String CLONE_INTRINSIC_SLOW_PATH = "(.*call,static.*java.lang.Object::clone.*" + END; protected static final String CLASS_CHECK_TRAP = START + "CallStaticJava" + MID + "uncommon_trap.*class_check" + END; protected static final String NULL_CHECK_TRAP = START + "CallStaticJava" + MID + "uncommon_trap.*null_check" + END; + protected static final String NULL_ASSERT_TRAP = START + "CallStaticJava" + MID + "uncommon_trap.*null_assert" + END; protected static final String RANGE_CHECK_TRAP = START + "CallStaticJava" + MID + "uncommon_trap.*range_check" + END; protected static final String UNHANDLED_TRAP = START + "CallStaticJava" + MID + "uncommon_trap.*unhandled" + END; protected static final String PREDICATE_TRAP = START + "CallStaticJava" + MID + "uncommon_trap.*predicate" + END; @@ -232,6 +236,7 @@ public abstract class InlineTypeTest { protected static final String CHECKCAST_ARRAYCOPY = "(.*call_leaf_nofp,runtime checkcast_arraycopy.*" + END; protected static final String JLONG_ARRAYCOPY = "(.*call_leaf_nofp,runtime jlong_disjoint_arraycopy.*" + END; protected static final String FIELD_ACCESS = "(.*Field: *" + END; + protected static final String SUBSTITUTABILITY_TEST = START + "CallStaticJava" + MID + "java.lang.invoke.ValueBootstrapMethods::isSubstitutable" + END; public static String[] concat(String prefix[], String... extra) { ArrayList list = new ArrayList(); @@ -264,6 +269,7 @@ public int getNumScenarios() { public String[] getVMParameters(int scenario) { switch (scenario) { case 0: return new String[] { + "-XX:-UseACmpProfile", "-XX:+AlwaysIncrementalInline", "-XX:FlatArrayElementMaxOops=5", "-XX:FlatArrayElementMaxSize=-1", @@ -272,6 +278,7 @@ public String[] getVMParameters(int scenario) { "-XX:+InlineTypePassFieldsAsArgs", "-XX:+InlineTypeReturnedAsFields"}; case 1: return new String[] { + "-XX:-UseACmpProfile", "-XX:-UseCompressedOops", "-XX:FlatArrayElementMaxOops=5", "-XX:FlatArrayElementMaxSize=-1", @@ -280,6 +287,7 @@ public String[] getVMParameters(int scenario) { "-XX:-InlineTypePassFieldsAsArgs", "-XX:-InlineTypeReturnedAsFields"}; case 2: return new String[] { + "-XX:-UseACmpProfile", "-XX:-UseCompressedOops", "-XX:FlatArrayElementMaxOops=0", "-XX:FlatArrayElementMaxSize=0", @@ -305,6 +313,7 @@ public String[] getVMParameters(int scenario) { "-XX:-InlineTypeReturnedAsFields", "-XX:-ReduceInitialCardMarks"}; case 5: return new String[] { + "-XX:-UseACmpProfile", "-XX:+AlwaysIncrementalInline", "-XX:FlatArrayElementMaxOops=5", "-XX:FlatArrayElementMaxSize=-1", @@ -503,6 +512,8 @@ static final class TestAnnotation { new TestAnnotation(ArrayLoadStoreProfileOff, () -> !UseArrayLoadStoreProfile), new TestAnnotation(TypeProfileOn, () -> TypeProfileLevel == 222), new TestAnnotation(TypeProfileOff, () -> TypeProfileLevel == 0), + new TestAnnotation(ACmpProfileOn, () -> UseACmpProfile), + new TestAnnotation(ACmpProfileOff, () -> !UseACmpProfile), }; private TestAnnotation(int flag, BooleanSupplier predicate) { @@ -822,10 +833,36 @@ public static void enqueueMethodForCompilation(Method m, int level) { WHITE_BOX.enqueueMethodForCompilation(m, level); } - // Unlike C2, C1 intrinsics never deoptimize System.arraycopy. Instead, we fall back to - // a normal method invocation when encountering flattened arrays. + enum TriState { + Maybe, + Yes, + No + } + + static private TriState compiledByC2(Method m) { + if (!USE_COMPILER || XCOMP || TEST_C1) { + return TriState.Maybe; + } + if (WHITE_BOX.isMethodCompiled(m, false) && + WHITE_BOX.getMethodCompilationLevel(m, false) >= COMP_LEVEL_FULL_OPTIMIZATION) { + return TriState.Yes; + } + return TriState.No; + } + static boolean isCompiledByC2(Method m) { - return USE_COMPILER && !XCOMP && WHITE_BOX.isMethodCompiled(m, false) && - WHITE_BOX.getMethodCompilationLevel(m, false) >= COMP_LEVEL_FULL_OPTIMIZATION; + return compiledByC2(m) == TriState.Yes; + } + + static void assertDeoptimizedByC2(Method m) { + if (compiledByC2(m) == TriState.Yes) { + throw new RuntimeException("Expected to have deoptimized"); + } + } + + static void assertCompiledByC2(Method m) { + if (compiledByC2(m) == TriState.No) { + throw new RuntimeException("Expected to be compiled"); + } } } diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrays.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrays.java index c53ecbd6879..0a13c97729e 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrays.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrays.java @@ -42,21 +42,6 @@ * compiler.valhalla.inlinetypes.TestArrays */ public class TestArrays extends InlineTypeTest { - // Unlike C2, C1 intrinsics never deoptimize System.arraycopy. Instead, we fall back to - // a normal method invocation when encountering flattened arrays. - private static void assertDeoptimizedByC2(Method m) { - int CompLevel_none = 0, // Interpreter - CompLevel_simple = 1, // C1 - CompLevel_limited_profile = 2, // C1, invocation & backedge counters - CompLevel_full_profile = 3, // C1, invocation & backedge counters + mdo - CompLevel_full_optimization = 4; // C2 or JVMCI - - if (USE_COMPILER && !XCOMP && !STRESS_CC && WHITE_BOX.isMethodCompiled(m, false) && - WHITE_BOX.getMethodCompilationLevel(m, false) >= CompLevel_full_optimization) { - throw new RuntimeException("Type check should have caused it to deoptimize"); - } - } - // Extra VM parameters for some test scenarios. See InlineTypeTest.getVMParameters() @Override public String[] getExtraVMParameters(int scenario) { diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorld.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorld.java index f0809298025..6beade6b9ce 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorld.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorld.java @@ -3384,4 +3384,30 @@ public void test123_verifier(boolean warmup) { // Expected } } + + // acmp doesn't need substitutability test when one input is known + // not to be a value type + @Test(failOn = SUBSTITUTABILITY_TEST) + public boolean test124(Integer o1, Object o2) { + return o1 == o2; + } + + @DontCompile + public void test124_verifier(boolean warmup) { + test124(42, 42); + test124(42, testValue1); + } + + // acmp doesn't need substitutability test when one input null + @Test(failOn = SUBSTITUTABILITY_TEST) + public boolean test125(Object o1) { + Object o2 = null; + return o1 == o2; + } + + @DontCompile + public void test125_verifier(boolean warmup) { + test125(testValue1); + test125(null); + } } diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorldProfiling.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorldProfiling.java index 6f29af80008..b1859d36356 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorldProfiling.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorldProfiling.java @@ -44,21 +44,27 @@ public class TestLWorldProfiling extends InlineTypeTest { static final String[][] scenarios = { {"-XX:-UseArrayLoadStoreProfile", + "-XX:-UseACmpProfile", "-XX:TypeProfileLevel=0", "-XX:-MonomorphicArrayCheck" }, { "-XX:+UseArrayLoadStoreProfile", + "-XX:+UseACmpProfile", "-XX:TypeProfileLevel=0" }, { "-XX:-UseArrayLoadStoreProfile", + "-XX:-UseACmpProfile", "-XX:TypeProfileLevel=222", "-XX:-MonomorphicArrayCheck" }, { "-XX:-UseArrayLoadStoreProfile", + "-XX:-UseACmpProfile", "-XX:TypeProfileLevel=0", "-XX:-MonomorphicArrayCheck", "-XX:-TieredCompilation" }, { "-XX:+UseArrayLoadStoreProfile", + "-XX:+UseACmpProfile", "-XX:TypeProfileLevel=0", "-XX:-TieredCompilation" }, { "-XX:-UseArrayLoadStoreProfile", + "-XX:-UseACmpProfile", "-XX:TypeProfileLevel=222", "-XX:-MonomorphicArrayCheck", "-XX:-TieredCompilation" } @@ -480,4 +486,431 @@ public void test20_verifier(boolean warmup) { test20(testIntegerArray, 42); test20(testNotFlattenableArray, notFlattenable); } + + // acmp tests + + // branch frequency profiling causes not equal branch to be optimized out + @Warmup(10000) + @Test(failOn = SUBSTITUTABILITY_TEST) + public boolean test21(Object o1, Object o2) { + return o1 == o2; + } + + @DontCompile + public void test21_verifier(boolean warmup) { + test21(42, 42); + test21(testValue1, testValue1); + } + + // Input profiled non null + @Warmup(10000) + @Test(valid = ACmpProfileOn, failOn = SUBSTITUTABILITY_TEST, match = { NULL_ASSERT_TRAP }, matchCount = { 1}) + @Test(match = { SUBSTITUTABILITY_TEST }, matchCount = { 1}) + public boolean test22(Object o1, Object o2) { + return o1 == o2; + } + + @DontCompile + public void test22_verifier(boolean warmup) { + test22(42, null); + test22(42.0, null); + if (!warmup) { + assertCompiledByC2(tests.get("TestLWorldProfiling::test22")); + test22(42, 42.0); + if (UseACmpProfile) { + assertDeoptimizedByC2(tests.get("TestLWorldProfiling::test22")); + } + } + } + + @Warmup(10000) + @Test(valid = ACmpProfileOn, failOn = SUBSTITUTABILITY_TEST, match = { NULL_ASSERT_TRAP }, matchCount = { 1}) + @Test(valid = TypeProfileOn, failOn = SUBSTITUTABILITY_TEST, match = { NULL_ASSERT_TRAP }, matchCount = { 1}) + @Test(match = { SUBSTITUTABILITY_TEST }, matchCount = { 1}) + public boolean test23(Object o1, Object o2) { + return o1 == o2; + } + + @DontCompile + public void test23_verifier(boolean warmup) { + test23(null, 42); + test23(null, 42.0); + if (!warmup) { + assertCompiledByC2(tests.get("TestLWorldProfiling::test23")); + test23(42, 42.0); + if (UseACmpProfile || TypeProfileLevel != 0) { + assertDeoptimizedByC2(tests.get("TestLWorldProfiling::test23")); + } + } + } + + @Warmup(10000) + @Test(valid = ACmpProfileOn, failOn = SUBSTITUTABILITY_TEST, match = { NULL_ASSERT_TRAP }, matchCount = { 1}) + @Test(match = { SUBSTITUTABILITY_TEST }, matchCount = { 1}) + public boolean test24(Object o1, Object o2) { + return o1 != o2; + } + + @DontCompile + public void test24_verifier(boolean warmup) { + test24(42, null); + test24(42.0, null); + if (!warmup) { + assertCompiledByC2(tests.get("TestLWorldProfiling::test24")); + test24(42, 42.0); + if (UseACmpProfile) { + assertDeoptimizedByC2(tests.get("TestLWorldProfiling::test24")); + } + } + } + + @Warmup(10000) + @Test(valid = ACmpProfileOn, failOn = SUBSTITUTABILITY_TEST, match = { NULL_ASSERT_TRAP }, matchCount = { 1}) + @Test(valid = TypeProfileOn, failOn = SUBSTITUTABILITY_TEST, match = { NULL_ASSERT_TRAP }, matchCount = { 1}) + @Test(match = { SUBSTITUTABILITY_TEST }, matchCount = { 1}) + public boolean test25(Object o1, Object o2) { + return o1 != o2; + } + + @DontCompile + public void test25_verifier(boolean warmup) { + test25(null, 42); + test25(null, 42.0); + if (!warmup) { + assertCompiledByC2(tests.get("TestLWorldProfiling::test25")); + test25(42, 42.0); + if (UseACmpProfile || TypeProfileLevel != 0) { + assertDeoptimizedByC2(tests.get("TestLWorldProfiling::test25")); + } + } + } + + // Input profiled not inline type with known type + @Warmup(10000) + @Test(valid = ACmpProfileOn, failOn = SUBSTITUTABILITY_TEST, match = { NULL_CHECK_TRAP, CLASS_CHECK_TRAP }, matchCount = { 1, 1}) + @Test(valid = TypeProfileOn, failOn = SUBSTITUTABILITY_TEST, match = { NULL_CHECK_TRAP, CLASS_CHECK_TRAP }, matchCount = { 1, 1}) + @Test(match = { SUBSTITUTABILITY_TEST }, matchCount = { 1}) + public boolean test26(Object o1, Object o2) { + return o1 == o2; + } + + @DontCompile + public void test26_verifier(boolean warmup) { + test26(42, 42); + test26(42, 42.0); + if (!warmup) { + assertCompiledByC2(tests.get("TestLWorldProfiling::test26")); + for (int i = 0; i < 10; i++) { + test26(42.0, 42); + } + if (UseACmpProfile || TypeProfileLevel != 0) { + assertDeoptimizedByC2(tests.get("TestLWorldProfiling::test26")); + } + } + } + + @Warmup(10000) + @Test(valid = ACmpProfileOn, failOn = SUBSTITUTABILITY_TEST, match = { NULL_CHECK_TRAP, CLASS_CHECK_TRAP }, matchCount = { 1, 1}) + @Test(match = { SUBSTITUTABILITY_TEST }, matchCount = { 1}) + public boolean test27(Object o1, Object o2) { + return o1 == o2; + } + + @DontCompile + public void test27_verifier(boolean warmup) { + test27(42, 42); + test27(42.0, 42); + if (!warmup) { + assertCompiledByC2(tests.get("TestLWorldProfiling::test27")); + for (int i = 0; i < 10; i++) { + test27(42, 42.0); + } + if (UseACmpProfile) { + assertDeoptimizedByC2(tests.get("TestLWorldProfiling::test27")); + } + } + } + + @Warmup(10000) + @Test(valid = ACmpProfileOn, failOn = SUBSTITUTABILITY_TEST, match = { NULL_CHECK_TRAP, CLASS_CHECK_TRAP }, matchCount = { 1, 1}) + @Test(valid = TypeProfileOn, failOn = SUBSTITUTABILITY_TEST, match = { NULL_CHECK_TRAP, CLASS_CHECK_TRAP }, matchCount = { 1, 1}) + @Test(match = { SUBSTITUTABILITY_TEST }, matchCount = { 1}) + public boolean test28(Object o1, Object o2) { + return o1 != o2; + } + + @DontCompile + public void test28_verifier(boolean warmup) { + test28(42, 42); + test28(42, 42.0); + if (!warmup) { + assertCompiledByC2(tests.get("TestLWorldProfiling::test28")); + for (int i = 0; i < 10; i++) { + test28(42.0, 42); + } + if (UseACmpProfile || TypeProfileLevel != 0) { + assertDeoptimizedByC2(tests.get("TestLWorldProfiling::test28")); + } + } + } + + @Warmup(10000) + @Test(valid = ACmpProfileOn, failOn = SUBSTITUTABILITY_TEST, match = { NULL_CHECK_TRAP, CLASS_CHECK_TRAP }, matchCount = { 1, 1}) + @Test(match = { SUBSTITUTABILITY_TEST }, matchCount = { 1}) + public boolean test29(Object o1, Object o2) { + return o1 != o2; + } + + @DontCompile + public void test29_verifier(boolean warmup) { + test29(42, 42); + test29(42.0, 42); + if (!warmup) { + assertCompiledByC2(tests.get("TestLWorldProfiling::test29")); + for (int i = 0; i < 10; i++) { + test29(42, 42.0); + } + if (UseACmpProfile) { + assertDeoptimizedByC2(tests.get("TestLWorldProfiling::test29")); + } + } + } + + @Warmup(10000) + @Test(valid = ACmpProfileOn, failOn = SUBSTITUTABILITY_TEST + NULL_CHECK_TRAP, match = { CLASS_CHECK_TRAP }, matchCount = { 1}) + @Test(valid = TypeProfileOn, failOn = SUBSTITUTABILITY_TEST + NULL_CHECK_TRAP, match = { CLASS_CHECK_TRAP }, matchCount = { 1}) + @Test(match = { SUBSTITUTABILITY_TEST }, matchCount = { 1}) + public boolean test30(Object o1, Object o2) { + return o1 == o2; + } + + @DontCompile + public void test30_verifier(boolean warmup) { + test30(42, 42); + test30(42, 42.0); + test30(null, 42); + if (!warmup) { + assertCompiledByC2(tests.get("TestLWorldProfiling::test30")); + for (int i = 0; i < 10; i++) { + test30(42.0, 42); + } + if (UseACmpProfile || TypeProfileLevel != 0) { + assertDeoptimizedByC2(tests.get("TestLWorldProfiling::test30")); + } + } + } + + @Warmup(10000) + @Test(valid = ACmpProfileOn, failOn = SUBSTITUTABILITY_TEST + NULL_CHECK_TRAP) + @Test(match = { SUBSTITUTABILITY_TEST }, matchCount = { 1}) + public boolean test31(Object o1, Object o2) { + return o1 == o2; + } + + @DontCompile + public void test31_verifier(boolean warmup) { + test31(42, 42); + test31(42.0, 42); + test31(42, null); + if (!warmup) { + assertCompiledByC2(tests.get("TestLWorldProfiling::test31")); + for (int i = 0; i < 10; i++) { + test31(42, 42.0); + } + if (UseACmpProfile) { + assertDeoptimizedByC2(tests.get("TestLWorldProfiling::test31")); + } + } + } + + // Input profiled not inline type with unknown type + @Warmup(10000) + @Test(valid = ACmpProfileOn, failOn = SUBSTITUTABILITY_TEST, match = { NULL_CHECK_TRAP, CLASS_CHECK_TRAP }, matchCount = { 1, 1}) + @Test(match = { SUBSTITUTABILITY_TEST }, matchCount = { 1}) + public boolean test32(Object o1, Object o2) { + return o1 == o2; + } + + @DontCompile + public void test32_verifier(boolean warmup) { + test32(42, 42); + test32(42, testValue1); + test32(42.0, 42); + if (!warmup) { + assertCompiledByC2(tests.get("TestLWorldProfiling::test32")); + for (int i = 0; i < 10; i++) { + test32(testValue1, 42); + } + if (UseACmpProfile) { + assertDeoptimizedByC2(tests.get("TestLWorldProfiling::test32")); + } + } + } + + @Warmup(10000) + @Test(valid = ACmpProfileOn, failOn = SUBSTITUTABILITY_TEST, match = { NULL_CHECK_TRAP, CLASS_CHECK_TRAP }, matchCount = { 1, 1}) + @Test(match = { SUBSTITUTABILITY_TEST }, matchCount = { 1}) + public boolean test33(Object o1, Object o2) { + return o1 == o2; + } + + @DontCompile + public void test33_verifier(boolean warmup) { + test33(42, 42); + test33(testValue1, 42); + test33(42, 42.0); + if (!warmup) { + assertCompiledByC2(tests.get("TestLWorldProfiling::test33")); + for (int i = 0; i < 10; i++) { + test33(42, testValue1); + } + if (UseACmpProfile) { + assertDeoptimizedByC2(tests.get("TestLWorldProfiling::test33")); + } + } + } + + @Warmup(10000) + @Test(valid = ACmpProfileOn, failOn = SUBSTITUTABILITY_TEST, match = { NULL_CHECK_TRAP, CLASS_CHECK_TRAP }, matchCount = { 1, 1}) + @Test(match = { SUBSTITUTABILITY_TEST }, matchCount = { 1}) + public boolean test34(Object o1, Object o2) { + return o1 != o2; + } + + @DontCompile + public void test34_verifier(boolean warmup) { + test34(42, 42); + test34(42, testValue1); + test34(42.0, 42); + if (!warmup) { + assertCompiledByC2(tests.get("TestLWorldProfiling::test34")); + for (int i = 0; i < 10; i++) { + test34(testValue1, 42); + } + if (UseACmpProfile) { + assertDeoptimizedByC2(tests.get("TestLWorldProfiling::test34")); + } + } + } + + @Warmup(10000) + @Test(valid = ACmpProfileOn, failOn = SUBSTITUTABILITY_TEST, match = { NULL_CHECK_TRAP, CLASS_CHECK_TRAP }, matchCount = { 1, 1}) + @Test(match = { SUBSTITUTABILITY_TEST }, matchCount = { 1}) + public boolean test35(Object o1, Object o2) { + return o1 != o2; + } + + @DontCompile + public void test35_verifier(boolean warmup) { + test35(42, 42); + test35(testValue1, 42); + test35(42, 42.0); + if (!warmup) { + assertCompiledByC2(tests.get("TestLWorldProfiling::test35")); + for (int i = 0; i < 10; i++) { + test35(42, testValue1); + } + if (UseACmpProfile) { + assertDeoptimizedByC2(tests.get("TestLWorldProfiling::test35")); + } + } + } + + @Warmup(10000) + @Test(valid = ACmpProfileOn, failOn = SUBSTITUTABILITY_TEST + NULL_CHECK_TRAP, match = { CLASS_CHECK_TRAP }, matchCount = { 1}) + @Test(match = { SUBSTITUTABILITY_TEST }, matchCount = { 1}) + public boolean test36(Object o1, Object o2) { + return o1 == o2; + } + + @DontCompile + public void test36_verifier(boolean warmup) { + test36(42, 42.0); + test36(42.0, testValue1); + test36(null, 42); + if (!warmup) { + assertCompiledByC2(tests.get("TestLWorldProfiling::test36")); + for (int i = 0; i < 10; i++) { + test36(testValue1, 42); + } + if (UseACmpProfile) { + assertDeoptimizedByC2(tests.get("TestLWorldProfiling::test36")); + } + } + } + + @Warmup(10000) + @Test(valid = ACmpProfileOn, failOn = SUBSTITUTABILITY_TEST + NULL_CHECK_TRAP) + @Test(match = { SUBSTITUTABILITY_TEST }, matchCount = { 1}) + public boolean test37(Object o1, Object o2) { + return o1 == o2; + } + + @DontCompile + public void test37_verifier(boolean warmup) { + test37(42.0, 42); + test37(testValue1, 42.0); + test37(42, null); + if (!warmup) { + assertCompiledByC2(tests.get("TestLWorldProfiling::test37")); + for (int i = 0; i < 10; i++) { + test37(42, testValue1); + } + if (UseACmpProfile) { + assertDeoptimizedByC2(tests.get("TestLWorldProfiling::test37")); + } + } + } + + // Test that acmp profile data that's unused at the acmp is fed to + // speculation and leverage later + @Warmup(10000) + @Test(valid = ACmpProfileOn, failOn = SUBSTITUTABILITY_TEST, match = { CLASS_CHECK_TRAP }, matchCount = { 1}) + @Test(valid = TypeProfileOn, failOn = SUBSTITUTABILITY_TEST, match = { CLASS_CHECK_TRAP }, matchCount = { 1}) + @Test(match = { SUBSTITUTABILITY_TEST }, matchCount = { 1 }) + public void test38(Object o1, Object o2, Object o3) { + if (o1 == o2) { + test38_helper2(); + } + test38_helper(o1, o3); + } + + public void test38_helper(Object o1, Object o2) { + if (o1 == o2) { + } + } + + public void test38_helper2() { + } + + @DontCompile + public void test38_verifier(boolean warmup) { + test38(42, 42, 42); + test38_helper(testValue1, testValue2); + } + + @Warmup(10000) + @Test(valid = ACmpProfileOn, failOn = SUBSTITUTABILITY_TEST, match = { CLASS_CHECK_TRAP }, matchCount = { 1}) + @Test(valid = TypeProfileOn, failOn = SUBSTITUTABILITY_TEST, match = { CLASS_CHECK_TRAP }, matchCount = { 1}) + @Test(match = { SUBSTITUTABILITY_TEST }, matchCount = { 1 }) + public void test39(Object o1, Object o2, Object o3) { + if (o1 == o2) { + test39_helper2(); + } + test39_helper(o2, o3); + } + + public void test39_helper(Object o1, Object o2) { + if (o1 == o2) { + } + } + + public void test39_helper2() { + } + + @DontCompile + public void test39_verifier(boolean warmup) { + test39(42, 42, 42); + test39_helper(testValue1, testValue2); + } } diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableArrays.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableArrays.java index d1107e2983c..84886be4777 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableArrays.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableArrays.java @@ -41,14 +41,6 @@ * compiler.valhalla.inlinetypes.TestNullableArrays */ public class TestNullableArrays extends InlineTypeTest { - // Unlike C2, C1 intrinsics never deoptimize System.arraycopy. Instead, we fall back to - // a normal method invocation when encountering flattened arrays. - private static void assertDeoptimizedByC2(Method m) { - if (isCompiledByC2(m)) { - throw new RuntimeException("Type check should have caused it to deoptimize"); - } - } - // Extra VM parameters for some test scenarios. See InlineTypeTest.getVMParameters() @Override public String[] getExtraVMParameters(int scenario) {