< prev index next >
src/hotspot/share/c1/c1_GraphBuilder.cpp
Print this page
*** 31,40 ****
--- 31,41 ----
#include "ci/ciCallSite.hpp"
#include "ci/ciField.hpp"
#include "ci/ciKlass.hpp"
#include "ci/ciMemberName.hpp"
#include "ci/ciUtilities.inline.hpp"
+ #include "ci/ciValueKlass.hpp"
#include "compiler/compileBroker.hpp"
#include "interpreter/bytecode.hpp"
#include "jfr/jfrEvents.hpp"
#include "memory/resourceArea.hpp"
#include "oops/oop.inline.hpp"
*** 646,655 ****
--- 647,667 ----
} else {
_fields.at(index)->kill();
}
}
+ // Record this newly allocated object
+ void new_instance(NewValueTypeInstance* object) {
+ int index = _newobjects.length();
+ _newobjects.append(object);
+ if (_fields.at_grow(index, NULL) == NULL) {
+ _fields.at_put(index, new FieldBuffer());
+ } else {
+ _fields.at(index)->kill();
+ }
+ }
+
void store_value(Value value) {
int index = _newobjects.find(value);
if (index != -1) {
// stored a newly allocated object into another object.
// Assume we've lost track of it as separate slice of memory.
*** 977,987 ****
--- 989,1011 ----
if (CSEArrayLength ||
(array->as_AccessField() && array->as_AccessField()->field()->is_constant()) ||
(array->as_NewArray() && array->as_NewArray()->length() && array->as_NewArray()->length()->type()->is_constant())) {
length = append(new ArrayLength(array, state_before));
}
+
+ if (array->is_loaded_flattened_array()) {
+ ciType* array_type = array->declared_type();
+ ciValueKlass* elem_klass = array_type->as_value_array_klass()->element_klass()->as_value_klass();
+ NewValueTypeInstance* new_instance = new NewValueTypeInstance(elem_klass, state_before, false);
+ _memory->new_instance(new_instance);
+ apush(append_split(new_instance));
+ LoadIndexed* load_indexed = new LoadIndexed(array, index, length, type, state_before);
+ load_indexed->set_vt(new_instance);
+ append(load_indexed);
+ } else {
push(as_ValueType(type), append(new LoadIndexed(array, index, length, type, state_before)));
+ }
}
void GraphBuilder::store_indexed(BasicType type) {
// In case of in block code motion in range check elimination
*** 1006,1015 ****
--- 1030,1040 ----
value = append(new LogicOp(Bytecodes::_iand, value, mask));
}
} else if (type == T_BYTE) {
check_boolean = true;
}
+
StoreIndexed* result = new StoreIndexed(array, index, length, type, value, state_before, check_boolean);
append(result);
_memory->store_value(value);
if (type == T_OBJECT && is_profiling()) {
*** 1617,1635 ****
--- 1642,1702 ----
default:
return new Constant(value);
}
}
+ void GraphBuilder::copy_value_content(ciValueKlass* vk, Value src, int src_off, Value dest, int dest_off,
+ ValueStack* state_before, bool needs_patching) {
+ for (int i = 0; i < vk->nof_nonstatic_fields(); i++) {
+ ciField* inner_field = vk->nonstatic_field_at(i);
+ assert(!inner_field->is_flattened(), "the iteration over nested fields is handled by the loop itself");
+ int off = inner_field->offset() - vk->first_field_offset();
+ LoadField* load = new LoadField(src, src_off + off, inner_field, false, state_before, needs_patching);
+ Value replacement = append(load);
+ StoreField* store = new StoreField(dest, dest_off + off, inner_field, replacement, false, state_before, needs_patching);
+ append(store);
+ }
+ }
+
void GraphBuilder::access_field(Bytecodes::Code code) {
bool will_link;
ciField* field = stream()->get_field(will_link);
ciInstanceKlass* holder = field->holder();
BasicType field_type = field->type()->basic_type();
ValueType* type = as_ValueType(field_type);
+
+ // Null check and deopt for getting static value field
+ ciValueKlass* value_klass = NULL;
+ Value default_value = NULL;
+ bool needs_deopt = false;
+ if (code == Bytecodes::_getstatic && !field->is_static_constant() &&
+ field->layout_type() == T_VALUETYPE && field->is_flattenable()) {
+ value_klass = field->type()->as_value_klass();
+ if (holder->is_loaded()) {
+ ciInstance* mirror = field->holder()->java_mirror();
+ ciObject* val = mirror->field_value(field).as_object();
+ if (val->is_null_object()) {
+ // This is a non-nullable static field, but it's not initialized.
+ // We need to do a null check, and replace it with the default value.
+ } else {
+ // No need to perform null check on this static field
+ value_klass = NULL;
+ }
+ }
+ if (value_klass != NULL) {
+ if (value_klass->is_loaded()) {
+ default_value = new Constant(new InstanceConstant(value_klass->default_value_instance()));
+ } else {
+ needs_deopt = true;
+ }
+ }
+ }
+
// call will_link again to determine if the field is valid.
const bool needs_patching = !holder->is_loaded() ||
!field->will_link(method(), code) ||
+ needs_deopt ||
PatchALot;
ValueStack* state_before = NULL;
if (!holder->is_initialized() || needs_patching) {
// save state before instruction for debug info when
*** 1673,1684 ****
push(type, append(constant));
} else {
if (state_before == NULL) {
state_before = copy_state_for_exception();
}
! push(type, append(new LoadField(append(obj), offset, field, true,
! state_before, needs_patching)));
}
break;
}
case Bytecodes::_putstatic: {
Value val = pop(type);
--- 1740,1756 ----
push(type, append(constant));
} else {
if (state_before == NULL) {
state_before = copy_state_for_exception();
}
! LoadField* load_field = new LoadField(append(obj), offset, field, true,
! state_before, needs_patching,
! value_klass, default_value);
! if (field->layout_type() == T_VALUETYPE && field->is_flattenable()) {
! load_field->set_never_null(true);
! }
! push(type, append(load_field));
}
break;
}
case Bytecodes::_putstatic: {
Value val = pop(type);
*** 1695,1705 ****
case Bytecodes::_getfield: {
// Check for compile-time constants, i.e., trusted final non-static fields.
Value constant = NULL;
obj = apop();
ObjectType* obj_type = obj->type()->as_ObjectType();
! if (field->is_constant() && obj_type->is_constant() && !PatchALot) {
ciObject* const_oop = obj_type->constant_value();
if (!const_oop->is_null_object() && const_oop->is_loaded()) {
ciConstant field_value = field->constant_value_of(const_oop);
if (field_value.is_valid()) {
constant = make_constant(field_value, field);
--- 1767,1777 ----
case Bytecodes::_getfield: {
// Check for compile-time constants, i.e., trusted final non-static fields.
Value constant = NULL;
obj = apop();
ObjectType* obj_type = obj->type()->as_ObjectType();
! if (field->is_constant() && !field->is_flattened() && obj_type->is_constant() && !PatchALot) {
ciObject* const_oop = obj_type->constant_value();
if (!const_oop->is_null_object() && const_oop->is_loaded()) {
ciConstant field_value = field->constant_value_of(const_oop);
if (field_value.is_valid()) {
constant = make_constant(field_value, field);
*** 1718,1735 ****
--- 1790,1822 ----
push(type, append(constant));
} else {
if (state_before == NULL) {
state_before = copy_state_for_exception();
}
+
+ if (!field->is_flattened()) {
LoadField* load = new LoadField(obj, offset, field, false, state_before, needs_patching);
Value replacement = !needs_patching ? _memory->load(load) : load;
if (replacement != load) {
assert(replacement->is_linked() || !replacement->can_be_linked(), "should already by linked");
push(type, replacement);
} else {
push(type, append(load));
}
+ } else { // flattened field, not optimized solution: re-instantiate the flattened value
+ assert(field->type()->is_valuetype(), "Sanity check");
+ ciValueKlass* value_klass = field->type()->as_value_klass();
+ int flattening_offset = field->offset() - value_klass->first_field_offset();
+ assert(field->type()->is_valuetype(), "Sanity check");
+ scope()->set_wrote_final();
+ scope()->set_wrote_fields();
+ NewValueTypeInstance* new_instance = new NewValueTypeInstance(value_klass, state_before, false);
+ _memory->new_instance(new_instance);
+ apush(append_split(new_instance));
+ copy_value_content(value_klass, obj, field->offset() , new_instance, value_klass->first_field_offset(),
+ state_before, needs_patching);
+ }
}
break;
}
case Bytecodes::_putfield: {
Value val = pop(type);
*** 1739,1761 ****
--- 1826,1920 ----
}
if (field->type()->basic_type() == T_BOOLEAN) {
Value mask = append(new Constant(new IntConstant(1)));
val = append(new LogicOp(Bytecodes::_iand, val, mask));
}
+
+ if (!field->is_flattened()) {
StoreField* store = new StoreField(obj, offset, field, val, false, state_before, needs_patching);
if (!needs_patching) store = _memory->store(store);
if (store != NULL) {
append(store);
}
+ } else {
+ assert(field->type()->is_valuetype(), "Sanity check");
+ ciValueKlass* value_klass = field->type()->as_value_klass();
+ int flattening_offset = field->offset() - value_klass->first_field_offset();
+ copy_value_content(value_klass, val, value_klass->first_field_offset(), obj, field->offset(),
+ state_before, needs_patching);
+ }
break;
}
default:
ShouldNotReachHere();
break;
}
}
+ // Baseline version of withfield, allocate every time
+ void GraphBuilder::withfield(int field_index)
+ {
+ bool will_link;
+ ciField* field_modify = stream()->get_field(will_link);
+ ciInstanceKlass* holder = field_modify->holder();
+ assert(holder->is_valuetype(), "must be a value klass");
+ BasicType field_type = field_modify->type()->basic_type();
+ ValueType* type = as_ValueType(field_type);
+
+ // call will_link again to determine if the field is valid.
+ const bool needs_patching = !holder->is_loaded() ||
+ !field_modify->will_link(method(), Bytecodes::_withfield) ||
+ PatchALot;
+
+
+ scope()->set_wrote_final();
+ scope()->set_wrote_fields();
+
+ const int offset = !needs_patching ? field_modify->offset() : -1;
+ Value val = pop(type);
+ Value obj = apop();
+
+ ValueStack* state_before = copy_state_for_exception();
+
+ NewValueTypeInstance* new_instance = new NewValueTypeInstance(holder->as_value_klass(), state_before, false);
+ _memory->new_instance(new_instance);
+ apush(append_split(new_instance));
+
+ for (int i = 0; i < holder->nof_nonstatic_fields(); i++) {
+ ciField* field = holder->nonstatic_field_at(i);
+ int off = field->offset();
+
+ if (field->offset() != offset) {
+ if (field->is_flattened()) {
+ assert(field->type()->is_valuetype(), "Sanity check");
+ assert(field->type()->is_valuetype(), "Only value types can be flattened");
+ ciValueKlass* vk = field->type()->as_value_klass();
+ copy_value_content(vk, obj, off, new_instance, vk->first_field_offset(), state_before, needs_patching);
+ } else {
+ // Only load those fields who are not modified
+ LoadField* load = new LoadField(obj, off, field, false, state_before, needs_patching);
+ Value replacement = append(load);
+ StoreField* store = new StoreField(new_instance, off, field, replacement, false, state_before, needs_patching);
+ append(store);
+ }
+ }
+ }
+
+ // Field to modify
+ if (field_modify->type()->basic_type() == T_BOOLEAN) {
+ Value mask = append(new Constant(new IntConstant(1)));
+ val = append(new LogicOp(Bytecodes::_iand, val, mask));
+ }
+ if (field_modify->is_flattened()) {
+ assert(field_modify->type()->is_valuetype(), "Only value types can be flattened");
+ ciValueKlass* vk = field_modify->type()->as_value_klass();
+ copy_value_content(vk, val, vk->first_field_offset(), new_instance, field_modify->offset(), state_before, needs_patching);
+ } else {
+ StoreField* store = new StoreField(new_instance, offset, field_modify, val, false, state_before, needs_patching);
+ append(store);
+ }
+ }
Dependencies* GraphBuilder::dependency_recorder() const {
assert(DeoptC1, "need debug information");
return compilation()->dependency_recorder();
}
*** 2109,2119 ****
profile_call(target, recv, target_klass, collect_args_for_profiling(args, NULL, false), false);
}
}
}
! Invoke* result = new Invoke(code, result_type, recv, args, vtable_index, target, state_before);
// push result
append_split(result);
if (result_type != voidType) {
if (method()->is_strict()) {
--- 2268,2279 ----
profile_call(target, recv, target_klass, collect_args_for_profiling(args, NULL, false), false);
}
}
}
! Invoke* result = new Invoke(code, result_type, recv, args, vtable_index, target, state_before,
! declared_signature->returns_never_null());
// push result
append_split(result);
if (result_type != voidType) {
if (method()->is_strict()) {
*** 2131,2145 ****
--- 2291,2316 ----
void GraphBuilder::new_instance(int klass_index) {
ValueStack* state_before = copy_state_exhandling();
bool will_link;
ciKlass* klass = stream()->get_klass(will_link);
assert(klass->is_instance_klass(), "must be an instance klass");
+ assert(!klass->is_valuetype(), "must not be a value klass");
NewInstance* new_instance = new NewInstance(klass->as_instance_klass(), state_before, stream()->is_unresolved_klass());
_memory->new_instance(new_instance);
apush(append_split(new_instance));
}
+ void GraphBuilder::new_value_type_instance(int klass_index) {
+ ValueStack* state_before = copy_state_exhandling();
+ bool will_link;
+ ciKlass* klass = stream()->get_klass(will_link);
+ assert(klass->is_valuetype(), "must be a value klass");
+ NewValueTypeInstance* new_instance = new NewValueTypeInstance(klass->as_value_klass(),
+ state_before, stream()->is_unresolved_klass());
+ _memory->new_instance(new_instance);
+ apush(append_split(new_instance));
+ }
void GraphBuilder::new_type_array() {
ValueStack* state_before = copy_state_exhandling();
apush(append_split(new NewTypeArray(ipop(), (BasicType)stream()->get_index(), state_before)));
}
*** 2148,2157 ****
--- 2319,2331 ----
void GraphBuilder::new_object_array() {
bool will_link;
ciKlass* klass = stream()->get_klass(will_link);
ValueStack* state_before = !klass->is_loaded() || PatchALot ? copy_state_before() : copy_state_exhandling();
NewArray* n = new NewObjectArray(klass, ipop(), state_before);
+ if (stream()->is_klass_never_null()) {
+ n->set_never_null(true);
+ }
apush(append_split(n));
}
bool GraphBuilder::direct_compare(ciKlass* k) {
*** 2172,2183 ****
void GraphBuilder::check_cast(int klass_index) {
bool will_link;
ciKlass* klass = stream()->get_klass(will_link);
ValueStack* state_before = !klass->is_loaded() || PatchALot ? copy_state_before() : copy_state_for_exception();
! CheckCast* c = new CheckCast(klass, apop(), state_before);
apush(append_split(c));
c->set_direct_compare(direct_compare(klass));
if (is_profiling()) {
// Note that we'd collect profile data in this method if we wanted it.
--- 2346,2358 ----
void GraphBuilder::check_cast(int klass_index) {
bool will_link;
ciKlass* klass = stream()->get_klass(will_link);
+ bool never_null = stream()->is_klass_never_null();
ValueStack* state_before = !klass->is_loaded() || PatchALot ? copy_state_before() : copy_state_for_exception();
! CheckCast* c = new CheckCast(klass, apop(), state_before, never_null);
apush(append_split(c));
c->set_direct_compare(direct_compare(klass));
if (is_profiling()) {
// Note that we'd collect profile data in this method if we wanted it.
*** 2212,2224 ****
}
}
void GraphBuilder::monitorenter(Value x, int bci) {
// save state before locking in case of deoptimization after a NullPointerException
ValueStack* state_before = copy_state_for_exception_with_bci(bci);
! append_with_bci(new MonitorEnter(x, state()->lock(x), state_before), bci);
kill_all();
}
void GraphBuilder::monitorexit(Value x, int bci) {
--- 2387,2418 ----
}
}
void GraphBuilder::monitorenter(Value x, int bci) {
+ bool maybe_valuetype = false;
+ if (bci == InvocationEntryBci) {
+ // Called by GraphBuilder::inline_sync_entry.
+ #ifdef ASSERT
+ ciType* obj_type = x->declared_type();
+ assert(obj_type == NULL || !obj_type->is_valuetype(), "valuetypes cannot have synchronized methods");
+ #endif
+ } else {
+ // We are compiling a monitorenter bytecode
+ if (EnableValhalla) {
+ ciType* obj_type = x->declared_type();
+ if (obj_type == NULL || obj_type->is_valuetype() || obj_type->as_klass()->is_java_lang_Object()) {
+ // If we're (possibly) locking on a valuetype, check for markOopDesc::always_locked_pattern
+ // and throw IMSE. (obj_type is null for Phi nodes, so let's just be conservative).
+ maybe_valuetype = true;
+ }
+ }
+ }
+
// save state before locking in case of deoptimization after a NullPointerException
ValueStack* state_before = copy_state_for_exception_with_bci(bci);
! append_with_bci(new MonitorEnter(x, state()->lock(x), state_before, maybe_valuetype), bci);
kill_all();
}
void GraphBuilder::monitorexit(Value x, int bci) {
*** 2870,2879 ****
--- 3064,3075 ----
case Bytecodes::_multianewarray : new_multi_array(s.cur_bcp()[3]); break;
case Bytecodes::_ifnull : if_null(objectType, If::eql); break;
case Bytecodes::_ifnonnull : if_null(objectType, If::neq); break;
case Bytecodes::_goto_w : _goto(s.cur_bci(), s.get_far_dest()); break;
case Bytecodes::_jsr_w : jsr(s.get_far_dest()); break;
+ case Bytecodes::_defaultvalue : new_value_type_instance(s.get_index_u2()); break;
+ case Bytecodes::_withfield : withfield(s.get_index_u2()); break;
case Bytecodes::_breakpoint : BAILOUT_("concurrent setting of breakpoint", NULL);
default : ShouldNotReachHere(); break;
}
if (log != NULL)
*** 3155,3177 ****
// Set up locals for receiver
int idx = 0;
if (!method()->is_static()) {
// we should always see the receiver
! state->store_local(idx, new Local(method()->holder(), objectType, idx, true));
idx = 1;
}
// Set up locals for incoming arguments
ciSignature* sig = method()->signature();
for (int i = 0; i < sig->count(); i++) {
ciType* type = sig->type_at(i);
BasicType basic_type = type->basic_type();
// don't allow T_ARRAY to propagate into locals types
! if (basic_type == T_ARRAY) basic_type = T_OBJECT;
ValueType* vt = as_ValueType(basic_type);
! state->store_local(idx, new Local(type, vt, idx, false));
idx += type->size();
}
// lock synchronized method
if (method()->is_synchronized()) {
--- 3351,3374 ----
// Set up locals for receiver
int idx = 0;
if (!method()->is_static()) {
// we should always see the receiver
! state->store_local(idx, new Local(method()->holder(), objectType, idx,
! /*receiver*/ true, /*never_null*/ method()->holder()->is_value_array_klass()));
idx = 1;
}
// Set up locals for incoming arguments
ciSignature* sig = method()->signature();
for (int i = 0; i < sig->count(); i++) {
ciType* type = sig->type_at(i);
BasicType basic_type = type->basic_type();
// don't allow T_ARRAY to propagate into locals types
! if (basic_type == T_ARRAY || basic_type == T_VALUETYPE) basic_type = T_OBJECT;
ValueType* vt = as_ValueType(basic_type);
! state->store_local(idx, new Local(type, vt, idx, false, sig->is_never_null_at(i)));
idx += type->size();
}
// lock synchronized method
if (method()->is_synchronized()) {
< prev index next >