< prev index next >

src/hotspot/share/opto/doCall.cpp

Print this page
*** 36,10 ***
--- 36,11 ---
  #include "logging/logStream.hpp"
  #include "opto/addnode.hpp"
  #include "opto/callGenerator.hpp"
  #include "opto/castnode.hpp"
  #include "opto/cfgnode.hpp"
+ #include "opto/inlinetypenode.hpp"
  #include "opto/mulnode.hpp"
  #include "opto/parse.hpp"
  #include "opto/rootnode.hpp"
  #include "opto/runtime.hpp"
  #include "opto/subnode.hpp"

*** 595,11 ***
      speculative_receiver_type = receiver_type != nullptr ? receiver_type->speculative_type() : nullptr;
    }
  
    // Additional receiver subtype checks for interface calls via invokespecial or invokeinterface.
    ciKlass* receiver_constraint = nullptr;
!   if (iter().cur_bc_raw() == Bytecodes::_invokespecial && !orig_callee->is_object_initializer()) {
      ciInstanceKlass* calling_klass = method()->holder();
      ciInstanceKlass* sender_klass = calling_klass;
      if (sender_klass->is_interface()) {
        receiver_constraint = sender_klass;
      }
--- 596,11 ---
      speculative_receiver_type = receiver_type != nullptr ? receiver_type->speculative_type() : nullptr;
    }
  
    // Additional receiver subtype checks for interface calls via invokespecial or invokeinterface.
    ciKlass* receiver_constraint = nullptr;
!   if (iter().cur_bc_raw() == Bytecodes::_invokespecial && !orig_callee->is_object_constructor()) {
      ciInstanceKlass* calling_klass = method()->holder();
      ciInstanceKlass* sender_klass = calling_klass;
      if (sender_klass->is_interface()) {
        receiver_constraint = sender_klass;
      }

*** 725,11 ***
        // Be careful here with return types.
        if (ctype != rtype) {
          BasicType rt = rtype->basic_type();
          BasicType ct = ctype->basic_type();
          if (ct == T_VOID) {
!           // It's OK for a method  to return a value that is discarded.
            // The discarding does not require any special action from the caller.
            // The Java code knows this, at VerifyType.isNullConversion.
            pop_node(rt);  // whatever it was, pop it
          } else if (rt == T_INT || is_subword_type(rt)) {
            // Nothing.  These cases are handled in lambda form bytecode.
--- 726,11 ---
        // Be careful here with return types.
        if (ctype != rtype) {
          BasicType rt = rtype->basic_type();
          BasicType ct = ctype->basic_type();
          if (ct == T_VOID) {
!           // It's OK for a method to return a value that is discarded.
            // The discarding does not require any special action from the caller.
            // The Java code knows this, at VerifyType.isNullConversion.
            pop_node(rt);  // whatever it was, pop it
          } else if (rt == T_INT || is_subword_type(rt)) {
            // Nothing.  These cases are handled in lambda form bytecode.

*** 783,10 ***
--- 784,63 ---
      }
      BasicType ct = ctype->basic_type();
      if (is_reference_type(ct)) {
        record_profiled_return_for_speculation();
      }
+     if (rtype->is_inlinetype() && !peek()->is_InlineType()) {
+       Node* retnode = pop();
+       retnode = InlineTypeNode::make_from_oop(this, retnode, rtype->as_inline_klass(), !gvn().type(retnode)->maybe_null());
+       push_node(T_OBJECT, retnode);
+     }
+ 
+     // Note that:
+     // - The caller map is the state just before the call of the currently parsed method with all arguments
+     //   on the stack. Therefore, we have caller_map->arg(0) == this.
+     // - local(0) contains the updated receiver after calling an inline type constructor.
+     // - Abstract value classes are not ciInlineKlass instances and thus abstract_value_klass->is_inlinetype() is false.
+     //   We use the bottom type of the receiver node to determine if we have a value class or not.
+     const bool is_current_method_inline_type_constructor =
+         // Is current method a constructor (i.e <init>)?
+         _method->is_object_constructor() &&
+         // Is the holder of the current constructor method an inline type?
+         _caller->map()->argument(_caller, 0)->bottom_type()->is_inlinetypeptr();
+     assert(!is_current_method_inline_type_constructor || !cg->method()->is_object_constructor() || receiver != nullptr,
+            "must have valid receiver after calling another constructor");
+     if (is_current_method_inline_type_constructor &&
+         // Is the just called method an inline type constructor?
+         cg->method()->is_object_constructor() && receiver->bottom_type()->is_inlinetypeptr() &&
+          // AND:
+          // 1) ... invoked on the same receiver? Then it's another constructor on the same object doing the initialization.
+         (receiver == _caller->map()->argument(_caller, 0) ||
+          // 2) ... abstract? Then it's the call to the super constructor which eventually calls Object.<init> to
+          //                    finish the initialization of this larval.
+          cg->method()->holder()->is_abstract() ||
+          // 3) ... Object.<init>? Then we know it's the final call to finish the larval initialization. Other
+          //        Object.<init> calls would have a non-inline-type receiver which we already excluded in the check above.
+          cg->method()->holder()->is_java_lang_Object())
+         ) {
+       assert(local(0)->is_InlineType() && receiver->bottom_type()->is_inlinetypeptr() && receiver->is_InlineType() &&
+              _caller->map()->argument(_caller, 0)->bottom_type()->inline_klass() == receiver->bottom_type()->inline_klass(),
+              "Unexpected receiver");
+       InlineTypeNode* updated_receiver = local(0)->as_InlineType();
+       InlineTypeNode* cloned_updated_receiver = updated_receiver->clone_if_required(&_gvn, _map);
+       cloned_updated_receiver->set_is_larval(false);
+       cloned_updated_receiver = _gvn.transform(cloned_updated_receiver)->as_InlineType();
+       // Receiver updated by the just called constructor. We need to update the map to make the effect visible. After
+       // the super() call, only the updated receiver in local(0) will be used from now on. Therefore, we do not need
+       // to update the original receiver 'receiver' but only the 'updated_receiver'.
+       replace_in_map(updated_receiver, cloned_updated_receiver);
+ 
+       if (_caller->has_method()) {
+         // If the current method is inlined, we also need to update the exit map to propagate the updated receiver
+         // to the caller map.
+         Node* receiver_in_caller = _caller->map()->argument(_caller, 0);
+         assert(receiver_in_caller->bottom_type()->inline_klass() == receiver->bottom_type()->inline_klass(),
+                "Receiver type mismatch");
+         _exits.map()->replace_edge(receiver_in_caller, cloned_updated_receiver, &_gvn);
+       }
+     }
    }
  
    // Restart record of parsing work after possible inlining of call
  #ifndef PRODUCT
    parse_histogram()->set_initial_state(bc());
< prev index next >