< prev index next >

src/hotspot/share/c1/c1_GraphBuilder.cpp

Print this page

        

@@ -31,10 +31,11 @@
 #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,10 +647,21 @@
     } 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,11 +989,23 @@
   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,10 +1030,11 @@
       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,19 +1642,61 @@
     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,12 +1740,17 @@
         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)));
+        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,11 +1767,11 @@
     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) {
+      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,18 +1790,33 @@
         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,23 +1826,95 @@
       }
       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,11 +2268,12 @@
         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);
+  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,15 +2291,26 @@
 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,10 +2319,13 @@
 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,12 +2346,13 @@
 
 
 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);
+  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,13 +2387,32 @@
   }
 }
 
 
 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), 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,10 +3064,12 @@
       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,23 +3351,24 @@
 
   // 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));
+    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_OBJECT;
+    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));
+    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 >