< prev index next > src/hotspot/share/opto/doCall.cpp
Print this page
#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"
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;
}
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;
}
// 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.
// 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.
}
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 >