6667620: (Escape Analysis) fix deoptimization for scalar replaced objects
Summary: Deoptimization code for reallocation and relocking scalar replaced objects has to be fixed.
Reviewed-by: rasbold, never
--- a/hotspot/src/share/vm/ci/ciInstanceKlass.cpp Tue Mar 11 11:04:40 2008 -0700
+++ b/hotspot/src/share/vm/ci/ciInstanceKlass.cpp Tue Mar 11 11:25:13 2008 -0700
@@ -34,7 +34,9 @@
// ciInstanceKlass::ciInstanceKlass
//
// Loaded instance klass.
-ciInstanceKlass::ciInstanceKlass(KlassHandle h_k) : ciKlass(h_k) {
+ciInstanceKlass::ciInstanceKlass(KlassHandle h_k) :
+ ciKlass(h_k), _non_static_fields(NULL)
+{
assert(get_Klass()->oop_is_instance(), "wrong type");
instanceKlass* ik = get_instanceKlass();
@@ -335,6 +337,37 @@
return field;
}
+// ------------------------------------------------------------------
+// ciInstanceKlass::non_static_fields.
+
+class NonStaticFieldFiller: public FieldClosure {
+ GrowableArray<ciField*>* _arr;
+ ciEnv* _curEnv;
+public:
+ NonStaticFieldFiller(ciEnv* curEnv, GrowableArray<ciField*>* arr) :
+ _curEnv(curEnv), _arr(arr)
+ {}
+ void do_field(fieldDescriptor* fd) {
+ ciField* field = new (_curEnv->arena()) ciField(fd);
+ _arr->append(field);
+ }
+};
+
+GrowableArray<ciField*>* ciInstanceKlass::non_static_fields() {
+ if (_non_static_fields == NULL) {
+ VM_ENTRY_MARK;
+ ciEnv* curEnv = ciEnv::current();
+ instanceKlass* ik = get_instanceKlass();
+ int max_n_fields = ik->fields()->length()/instanceKlass::next_offset;
+
+ _non_static_fields =
+ new (curEnv->arena()) GrowableArray<ciField*>(max_n_fields);
+ NonStaticFieldFiller filler(curEnv, _non_static_fields);
+ ik->do_nonstatic_fields(&filler);
+ }
+ return _non_static_fields;
+}
+
static int sort_field_by_offset(ciField** a, ciField** b) {
return (*a)->offset_in_bytes() - (*b)->offset_in_bytes();
// (no worries about 32-bit overflow...)
--- a/hotspot/src/share/vm/ci/ciInstanceKlass.hpp Tue Mar 11 11:04:40 2008 -0700
+++ b/hotspot/src/share/vm/ci/ciInstanceKlass.hpp Tue Mar 11 11:25:13 2008 -0700
@@ -46,6 +46,7 @@
bool _has_subklass;
ciFlags _flags;
jint _nonstatic_field_size;
+ jint _nonstatic_oop_map_size;
// Lazy fields get filled in only upon request.
ciInstanceKlass* _super;
@@ -58,6 +59,8 @@
ciInstanceKlass* _implementors[implementors_limit];
jint _nof_implementors;
+ GrowableArray<ciField*>* _non_static_fields;
+
protected:
ciInstanceKlass(KlassHandle h_k);
ciInstanceKlass(ciSymbol* name, jobject loader, jobject protection_domain);
@@ -129,6 +132,9 @@
jint nonstatic_field_size() {
assert(is_loaded(), "must be loaded");
return _nonstatic_field_size; }
+ jint nonstatic_oop_map_size() {
+ assert(is_loaded(), "must be loaded");
+ return _nonstatic_oop_map_size; }
ciInstanceKlass* super();
jint nof_implementors() {
assert(is_loaded(), "must be loaded");
@@ -138,6 +144,9 @@
ciInstanceKlass* get_canonical_holder(int offset);
ciField* get_field_by_offset(int field_offset, bool is_static);
+
+ GrowableArray<ciField*>* non_static_fields();
+
// total number of nonstatic fields (including inherited):
int nof_nonstatic_fields() {
if (_nonstatic_fields == NULL)
--- a/hotspot/src/share/vm/code/debugInfo.cpp Tue Mar 11 11:04:40 2008 -0700
+++ b/hotspot/src/share/vm/code/debugInfo.cpp Tue Mar 11 11:25:13 2008 -0700
@@ -47,7 +47,8 @@
}
#endif
ObjectValue* result = new ObjectValue(id);
- _obj_pool->append(result);
+ // Cache the object since an object field could reference it.
+ _obj_pool->push(result);
result->read_object(this);
return result;
}
@@ -56,9 +57,9 @@
int id = read_int();
assert(_obj_pool != NULL, "object pool does not exist");
for (int i = _obj_pool->length() - 1; i >= 0; i--) {
- ObjectValue* sv = (ObjectValue*) _obj_pool->at(i);
- if (sv->id() == id) {
- return sv;
+ ObjectValue* ov = (ObjectValue*) _obj_pool->at(i);
+ if (ov->id() == id) {
+ return ov;
}
}
ShouldNotReachHere();
--- a/hotspot/src/share/vm/code/scopeDesc.cpp Tue Mar 11 11:04:40 2008 -0700
+++ b/hotspot/src/share/vm/code/scopeDesc.cpp Tue Mar 11 11:25:13 2008 -0700
@@ -91,7 +91,9 @@
DebugInfoReadStream* stream = new DebugInfoReadStream(_code, decode_offset, result);
int length = stream->read_int();
for (int index = 0; index < length; index++) {
- result->push(ScopeValue::read_from(stream));
+ // Objects values are pushed to 'result' array during read so that
+ // object's fields could reference it (OBJECT_ID_CODE).
+ (void)ScopeValue::read_from(stream);
}
assert(result->length() == length, "inconsistent debug information");
return result;
--- a/hotspot/src/share/vm/oops/instanceKlass.cpp Tue Mar 11 11:04:40 2008 -0700
+++ b/hotspot/src/share/vm/oops/instanceKlass.cpp Tue Mar 11 11:25:13 2008 -0700
@@ -791,17 +791,39 @@
}
+static int compare_fields_by_offset(int* a, int* b) {
+ return a[0] - b[0];
+}
+
void instanceKlass::do_nonstatic_fields(FieldClosure* cl) {
- fieldDescriptor fd;
instanceKlass* super = superklass();
if (super != NULL) {
super->do_nonstatic_fields(cl);
}
+ fieldDescriptor fd;
int length = fields()->length();
+ // In DebugInfo nonstatic fields are sorted by offset.
+ int* fields_sorted = NEW_C_HEAP_ARRAY(int, 2*(length+1));
+ int j = 0;
for (int i = 0; i < length; i += next_offset) {
fd.initialize(as_klassOop(), i);
- if (!(fd.is_static())) cl->do_field(&fd);
+ if (!fd.is_static()) {
+ fields_sorted[j + 0] = fd.offset();
+ fields_sorted[j + 1] = i;
+ j += 2;
+ }
}
+ if (j > 0) {
+ length = j;
+ // _sort_Fn is defined in growableArray.hpp.
+ qsort(fields_sorted, length/2, 2*sizeof(int), (_sort_Fn)compare_fields_by_offset);
+ for (int i = 0; i < length; i += 2) {
+ fd.initialize(as_klassOop(), fields_sorted[i + 1]);
+ assert(!fd.is_static() && fd.offset() == fields_sorted[i], "only nonstatic fields");
+ cl->do_field(&fd);
+ }
+ }
+ FREE_C_HEAP_ARRAY(int, fields_sorted);
}
--- a/hotspot/src/share/vm/runtime/deoptimization.cpp Tue Mar 11 11:04:40 2008 -0700
+++ b/hotspot/src/share/vm/runtime/deoptimization.cpp Tue Mar 11 11:25:13 2008 -0700
@@ -141,41 +141,45 @@
#ifdef COMPILER2
// Reallocate the non-escaping objects and restore their fields. Then
// relock objects if synchronization on them was eliminated.
- if (DoEscapeAnalysis && EliminateAllocations) {
- GrowableArray<ScopeValue*>* objects = chunk->at(0)->scope()->objects();
- bool reallocated = false;
- if (objects != NULL) {
- JRT_BLOCK
- reallocated = realloc_objects(thread, &deoptee, objects, THREAD);
- JRT_END
- }
- if (reallocated) {
- reassign_fields(&deoptee, &map, objects);
-#ifndef PRODUCT
- if (TraceDeoptimization) {
- ttyLocker ttyl;
- tty->print_cr("REALLOC OBJECTS in thread " INTPTR_FORMAT, thread);
- print_objects(objects);
+ if (DoEscapeAnalysis) {
+ if (EliminateAllocations) {
+ GrowableArray<ScopeValue*>* objects = chunk->at(0)->scope()->objects();
+ bool reallocated = false;
+ if (objects != NULL) {
+ JRT_BLOCK
+ reallocated = realloc_objects(thread, &deoptee, objects, THREAD);
+ JRT_END
}
-#endif
- }
- for (int i = 0; i < chunk->length(); i++) {
- GrowableArray<MonitorValue*>* monitors = chunk->at(i)->scope()->monitors();
- if (monitors != NULL) {
- relock_objects(&deoptee, &map, monitors);
+ if (reallocated) {
+ reassign_fields(&deoptee, &map, objects);
#ifndef PRODUCT
if (TraceDeoptimization) {
ttyLocker ttyl;
- tty->print_cr("RELOCK OBJECTS in thread " INTPTR_FORMAT, thread);
- for (int j = 0; i < monitors->length(); i++) {
- MonitorValue* mv = monitors->at(i);
- if (mv->eliminated()) {
- StackValue* owner = StackValue::create_stack_value(&deoptee, &map, mv->owner());
- tty->print_cr(" object <" INTPTR_FORMAT "> locked", owner->get_obj()());
+ tty->print_cr("REALLOC OBJECTS in thread " INTPTR_FORMAT, thread);
+ print_objects(objects);
+ }
+#endif
+ }
+ }
+ if (EliminateLocks) {
+ for (int i = 0; i < chunk->length(); i++) {
+ GrowableArray<MonitorValue*>* monitors = chunk->at(i)->scope()->monitors();
+ if (monitors != NULL) {
+ relock_objects(&deoptee, &map, monitors);
+#ifndef PRODUCT
+ if (TraceDeoptimization) {
+ ttyLocker ttyl;
+ tty->print_cr("RELOCK OBJECTS in thread " INTPTR_FORMAT, thread);
+ for (int j = 0; j < monitors->length(); j++) {
+ MonitorValue* mv = monitors->at(j);
+ if (mv->eliminated()) {
+ StackValue* owner = StackValue::create_stack_value(&deoptee, &map, mv->owner());
+ tty->print_cr(" object <" INTPTR_FORMAT "> locked", owner->get_obj()());
+ }
}
}
+#endif
}
-#endif
}
}
}
@@ -656,6 +660,7 @@
void do_field(fieldDescriptor* fd) {
+ intptr_t val;
StackValue* value =
StackValue::create_stack_value(_fr, _reg_map, _sv->field_at(i()));
int offset = fd->offset();
@@ -669,24 +674,36 @@
assert(value->type() == T_INT, "Agreement.");
StackValue* low =
StackValue::create_stack_value(_fr, _reg_map, _sv->field_at(++_i));
+#ifdef _LP64
+ jlong res = (jlong)low->get_int();
+#else
+#ifdef SPARC
+ // For SPARC we have to swap high and low words.
+ jlong res = jlong_from((jint)low->get_int(), (jint)value->get_int());
+#else
jlong res = jlong_from((jint)value->get_int(), (jint)low->get_int());
+#endif //SPARC
+#endif
_obj->long_field_put(offset, res);
break;
}
-
+ // Have to cast to INT (32 bits) pointer to avoid little/big-endian problem.
case T_INT: case T_FLOAT: // 4 bytes.
assert(value->type() == T_INT, "Agreement.");
- _obj->int_field_put(offset, (jint)value->get_int());
+ val = value->get_int();
+ _obj->int_field_put(offset, (jint)*((jint*)&val));
break;
case T_SHORT: case T_CHAR: // 2 bytes
assert(value->type() == T_INT, "Agreement.");
- _obj->short_field_put(offset, (jshort)value->get_int());
+ val = value->get_int();
+ _obj->short_field_put(offset, (jshort)*((jint*)&val));
break;
- case T_BOOLEAN: // 1 byte
+ case T_BOOLEAN: case T_BYTE: // 1 byte
assert(value->type() == T_INT, "Agreement.");
- _obj->bool_field_put(offset, (jboolean)value->get_int());
+ val = value->get_int();
+ _obj->bool_field_put(offset, (jboolean)*((jint*)&val));
break;
default:
@@ -698,25 +715,49 @@
// restore elements of an eliminated type array
void Deoptimization::reassign_type_array_elements(frame* fr, RegisterMap* reg_map, ObjectValue* sv, typeArrayOop obj, BasicType type) {
- StackValue* low;
- jlong lval;
int index = 0;
+ intptr_t val;
for (int i = 0; i < sv->field_size(); i++) {
StackValue* value = StackValue::create_stack_value(fr, reg_map, sv->field_at(i));
switch(type) {
- case T_BOOLEAN: obj->bool_at_put (index, (jboolean) value->get_int()); break;
- case T_BYTE: obj->byte_at_put (index, (jbyte) value->get_int()); break;
- case T_CHAR: obj->char_at_put (index, (jchar) value->get_int()); break;
- case T_SHORT: obj->short_at_put(index, (jshort) value->get_int()); break;
- case T_INT: obj->int_at_put (index, (jint) value->get_int()); break;
- case T_FLOAT: obj->float_at_put(index, (jfloat) value->get_int()); break;
- case T_LONG:
- case T_DOUBLE:
- low = StackValue::create_stack_value(fr, reg_map, sv->field_at(++i));
- lval = jlong_from((jint)value->get_int(), (jint)low->get_int());
- sv->value()->long_field_put(index, lval);
- break;
+ case T_LONG: case T_DOUBLE: {
+ assert(value->type() == T_INT, "Agreement.");
+ StackValue* low =
+ StackValue::create_stack_value(fr, reg_map, sv->field_at(++i));
+#ifdef _LP64
+ jlong res = (jlong)low->get_int();
+#else
+#ifdef SPARC
+ // For SPARC we have to swap high and low words.
+ jlong res = jlong_from((jint)low->get_int(), (jint)value->get_int());
+#else
+ jlong res = jlong_from((jint)value->get_int(), (jint)low->get_int());
+#endif //SPARC
+#endif
+ obj->long_at_put(index, res);
+ break;
+ }
+
+ // Have to cast to INT (32 bits) pointer to avoid little/big-endian problem.
+ case T_INT: case T_FLOAT: // 4 bytes.
+ assert(value->type() == T_INT, "Agreement.");
+ val = value->get_int();
+ obj->int_at_put(index, (jint)*((jint*)&val));
+ break;
+
+ case T_SHORT: case T_CHAR: // 2 bytes
+ assert(value->type() == T_INT, "Agreement.");
+ val = value->get_int();
+ obj->short_at_put(index, (jshort)*((jint*)&val));
+ break;
+
+ case T_BOOLEAN: case T_BYTE: // 1 byte
+ assert(value->type() == T_INT, "Agreement.");
+ val = value->get_int();
+ obj->bool_at_put(index, (jboolean)*((jint*)&val));
+ break;
+
default:
ShouldNotReachHere();
}