|
25 | 25 | #include "ci/ciCallSite.hpp"
|
26 | 26 | #include "ci/ciMethodHandle.hpp"
|
27 | 27 | #include "ci/ciSymbols.hpp"
|
| 28 | +#include "classfile/vmIntrinsics.hpp" |
28 | 29 | #include "classfile/vmSymbols.hpp"
|
29 | 30 | #include "compiler/compileBroker.hpp"
|
30 | 31 | #include "compiler/compileLog.hpp"
|
@@ -88,6 +89,64 @@ static void trace_type_profile(Compile* C, ciMethod* method, JVMState* jvms,
|
88 | 89 | }
|
89 | 90 | }
|
90 | 91 |
|
| 92 | +static bool arg_can_be_larval(ciMethod* callee, int arg_idx) { |
| 93 | + if (callee->is_object_constructor() && arg_idx == 0) { |
| 94 | + return true; |
| 95 | + } |
| 96 | + |
| 97 | + if (arg_idx != 1 || callee->intrinsic_id() == vmIntrinsicID::_none) { |
| 98 | + return false; |
| 99 | + } |
| 100 | + |
| 101 | + switch (callee->intrinsic_id()) { |
| 102 | + case vmIntrinsicID::_finishPrivateBuffer: |
| 103 | + case vmIntrinsicID::_putBoolean: |
| 104 | + case vmIntrinsicID::_putBooleanOpaque: |
| 105 | + case vmIntrinsicID::_putBooleanRelease: |
| 106 | + case vmIntrinsicID::_putBooleanVolatile: |
| 107 | + case vmIntrinsicID::_putByte: |
| 108 | + case vmIntrinsicID::_putByteOpaque: |
| 109 | + case vmIntrinsicID::_putByteRelease: |
| 110 | + case vmIntrinsicID::_putByteVolatile: |
| 111 | + case vmIntrinsicID::_putChar: |
| 112 | + case vmIntrinsicID::_putCharOpaque: |
| 113 | + case vmIntrinsicID::_putCharRelease: |
| 114 | + case vmIntrinsicID::_putCharUnaligned: |
| 115 | + case vmIntrinsicID::_putCharVolatile: |
| 116 | + case vmIntrinsicID::_putShort: |
| 117 | + case vmIntrinsicID::_putShortOpaque: |
| 118 | + case vmIntrinsicID::_putShortRelease: |
| 119 | + case vmIntrinsicID::_putShortUnaligned: |
| 120 | + case vmIntrinsicID::_putShortVolatile: |
| 121 | + case vmIntrinsicID::_putInt: |
| 122 | + case vmIntrinsicID::_putIntOpaque: |
| 123 | + case vmIntrinsicID::_putIntRelease: |
| 124 | + case vmIntrinsicID::_putIntUnaligned: |
| 125 | + case vmIntrinsicID::_putIntVolatile: |
| 126 | + case vmIntrinsicID::_putLong: |
| 127 | + case vmIntrinsicID::_putLongOpaque: |
| 128 | + case vmIntrinsicID::_putLongRelease: |
| 129 | + case vmIntrinsicID::_putLongUnaligned: |
| 130 | + case vmIntrinsicID::_putLongVolatile: |
| 131 | + case vmIntrinsicID::_putFloat: |
| 132 | + case vmIntrinsicID::_putFloatOpaque: |
| 133 | + case vmIntrinsicID::_putFloatRelease: |
| 134 | + case vmIntrinsicID::_putFloatVolatile: |
| 135 | + case vmIntrinsicID::_putDouble: |
| 136 | + case vmIntrinsicID::_putDoubleOpaque: |
| 137 | + case vmIntrinsicID::_putDoubleRelease: |
| 138 | + case vmIntrinsicID::_putDoubleVolatile: |
| 139 | + case vmIntrinsicID::_putReference: |
| 140 | + case vmIntrinsicID::_putReferenceOpaque: |
| 141 | + case vmIntrinsicID::_putReferenceRelease: |
| 142 | + case vmIntrinsicID::_putReferenceVolatile: |
| 143 | + case vmIntrinsicID::_putValue: |
| 144 | + return true; |
| 145 | + default: |
| 146 | + return false; |
| 147 | + } |
| 148 | +} |
| 149 | + |
91 | 150 | CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool call_does_dispatch,
|
92 | 151 | JVMState* jvms, bool allow_inline,
|
93 | 152 | float prof_factor, ciKlass* speculative_receiver_type,
|
@@ -645,6 +704,15 @@ void Parse::do_call() {
|
645 | 704 | set_stack(sp() - nargs, casted_receiver);
|
646 | 705 | }
|
647 | 706 |
|
| 707 | + // Scalarize value objects passed into this invocation if we know that they are not larval |
| 708 | + for (int arg_idx = 0; arg_idx < nargs; arg_idx++) { |
| 709 | + if (arg_can_be_larval(callee, arg_idx)) { |
| 710 | + continue; |
| 711 | + } |
| 712 | + |
| 713 | + cast_to_non_larval(peek(nargs - 1 - arg_idx)); |
| 714 | + } |
| 715 | + |
648 | 716 | // Note: It's OK to try to inline a virtual call.
|
649 | 717 | // The call generator will not attempt to inline a polymorphic call
|
650 | 718 | // unless it knows how to optimize the receiver dispatch.
|
@@ -807,59 +875,27 @@ void Parse::do_call() {
|
807 | 875 | if (is_reference_type(ct)) {
|
808 | 876 | record_profiled_return_for_speculation();
|
809 | 877 | }
|
810 |
| - if (rtype->is_inlinetype() && !peek()->is_InlineType()) { |
811 |
| - Node* retnode = pop(); |
812 |
| - retnode = InlineTypeNode::make_from_oop(this, retnode, rtype->as_inline_klass()); |
813 |
| - push_node(T_OBJECT, retnode); |
814 |
| - } |
815 | 878 |
|
816 |
| - // Note that: |
817 |
| - // - The caller map is the state just before the call of the currently parsed method with all arguments |
818 |
| - // on the stack. Therefore, we have caller_map->arg(0) == this. |
819 |
| - // - local(0) contains the updated receiver after calling an inline type constructor. |
820 |
| - // - Abstract value classes are not ciInlineKlass instances and thus abstract_value_klass->is_inlinetype() is false. |
821 |
| - // We use the bottom type of the receiver node to determine if we have a value class or not. |
822 |
| - const bool is_current_method_inline_type_constructor = |
823 |
| - // Is current method a constructor (i.e <init>)? |
824 |
| - _method->is_object_constructor() && |
825 |
| - // Is the holder of the current constructor method an inline type? |
826 |
| - _caller->map()->argument(_caller, 0)->bottom_type()->is_inlinetypeptr(); |
827 |
| - assert(!is_current_method_inline_type_constructor || !cg->method()->is_object_constructor() || receiver != nullptr, |
828 |
| - "must have valid receiver after calling another constructor"); |
829 |
| - if (is_current_method_inline_type_constructor && |
830 |
| - // Is the just called method an inline type constructor? |
831 |
| - cg->method()->is_object_constructor() && receiver->bottom_type()->is_inlinetypeptr() && |
832 |
| - // AND: |
833 |
| - // 1) ... invoked on the same receiver? Then it's another constructor on the same object doing the initialization. |
834 |
| - (receiver == _caller->map()->argument(_caller, 0) || |
835 |
| - // 2) ... abstract? Then it's the call to the super constructor which eventually calls Object.<init> to |
836 |
| - // finish the initialization of this larval. |
837 |
| - cg->method()->holder()->is_abstract() || |
838 |
| - // 3) ... Object.<init>? Then we know it's the final call to finish the larval initialization. Other |
839 |
| - // Object.<init> calls would have a non-inline-type receiver which we already excluded in the check above. |
840 |
| - cg->method()->holder()->is_java_lang_Object()) |
841 |
| - ) { |
842 |
| - assert(local(0)->is_InlineType() && receiver->bottom_type()->is_inlinetypeptr() && receiver->is_InlineType() && |
843 |
| - _caller->map()->argument(_caller, 0)->bottom_type()->inline_klass() == receiver->bottom_type()->inline_klass(), |
844 |
| - "Unexpected receiver"); |
845 |
| - InlineTypeNode* updated_receiver = local(0)->as_InlineType(); |
846 |
| - InlineTypeNode* cloned_updated_receiver = updated_receiver->clone_if_required(&_gvn, _map); |
847 |
| - cloned_updated_receiver->set_is_larval(false); |
848 |
| - cloned_updated_receiver = _gvn.transform(cloned_updated_receiver)->as_InlineType(); |
849 |
| - // Receiver updated by the just called constructor. We need to update the map to make the effect visible. After |
850 |
| - // the super() call, only the updated receiver in local(0) will be used from now on. Therefore, we do not need |
851 |
| - // to update the original receiver 'receiver' but only the 'updated_receiver'. |
852 |
| - replace_in_map(updated_receiver, cloned_updated_receiver); |
853 |
| - |
854 |
| - if (_caller->has_method()) { |
855 |
| - // If the current method is inlined, we also need to update the exit map to propagate the updated receiver |
856 |
| - // to the caller map. |
857 |
| - Node* receiver_in_caller = _caller->map()->argument(_caller, 0); |
858 |
| - assert(receiver_in_caller->bottom_type()->inline_klass() == receiver->bottom_type()->inline_klass(), |
859 |
| - "Receiver type mismatch"); |
860 |
| - _exits.map()->replace_edge(receiver_in_caller, cloned_updated_receiver, &_gvn); |
| 879 | + if (!rtype->is_void() && cg->method()->intrinsic_id() != vmIntrinsicID::_makePrivateBuffer) { |
| 880 | + Node* retnode = peek(); |
| 881 | + const Type* rettype = gvn().type(retnode); |
| 882 | + if (rettype->is_inlinetypeptr() && !retnode->is_InlineType()) { |
| 883 | + retnode = InlineTypeNode::make_from_oop(this, retnode, rettype->inline_klass()); |
| 884 | + dec_sp(1); |
| 885 | + push(retnode); |
861 | 886 | }
|
862 | 887 | }
|
| 888 | + |
| 889 | + if (cg->method()->is_object_constructor() && receiver != nullptr && gvn().type(receiver)->is_inlinetypeptr()) { |
| 890 | + InlineTypeNode* non_larval = InlineTypeNode::make_from_oop(this, receiver, gvn().type(receiver)->inline_klass()); |
| 891 | + // Relinquish the oop input, we will delay the allocation to the point it is needed, see the |
| 892 | + // comments in InlineTypeNode::Ideal for more details |
| 893 | + non_larval = non_larval->clone_if_required(&gvn(), nullptr); |
| 894 | + non_larval->set_oop(gvn(), null()); |
| 895 | + non_larval->set_is_buffered(gvn(), false); |
| 896 | + non_larval = gvn().transform(non_larval)->as_InlineType(); |
| 897 | + map()->replace_edge(receiver, non_larval); |
| 898 | + } |
863 | 899 | }
|
864 | 900 |
|
865 | 901 | // Restart record of parsing work after possible inlining of call
|
|
0 commit comments