Skip to content

Commit a53345a

Browse files
Cesar Soares LucasVladimir Kozlov
authored andcommitted
8287061: Support for rematerializing scalar replaced objects participating in allocation merges
Reviewed-by: kvn, vlivanov
1 parent 3236ba0 commit a53345a

File tree

26 files changed

+2625
-248
lines changed

26 files changed

+2625
-248
lines changed

src/hotspot/share/code/debugInfo.cpp

Lines changed: 112 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "gc/shared/collectedHeap.hpp"
3030
#include "memory/universe.hpp"
3131
#include "oops/oop.inline.hpp"
32+
#include "runtime/stackValue.hpp"
3233
#include "runtime/handles.inline.hpp"
3334
#include "runtime/interfaceSupport.inline.hpp"
3435
#include "runtime/javaThread.hpp"
@@ -80,6 +81,20 @@ ScopeValue* DebugInfoReadStream::read_object_value(bool is_auto_box) {
8081
return result;
8182
}
8283

84+
ScopeValue* DebugInfoReadStream::read_object_merge_value() {
85+
int id = read_int();
86+
#ifdef ASSERT
87+
assert(_obj_pool != nullptr, "object pool does not exist");
88+
for (int i = _obj_pool->length() - 1; i >= 0; i--) {
89+
assert(_obj_pool->at(i)->as_ObjectValue()->id() != id, "should not be read twice");
90+
}
91+
#endif
92+
ObjectMergeValue* result = new ObjectMergeValue(id);
93+
_obj_pool->push(result);
94+
result->read_object(this);
95+
return result;
96+
}
97+
8398
ScopeValue* DebugInfoReadStream::get_cached_object() {
8499
int id = read_int();
85100
assert(_obj_pool != nullptr, "object pool does not exist");
@@ -98,7 +113,8 @@ ScopeValue* DebugInfoReadStream::get_cached_object() {
98113
enum { LOCATION_CODE = 0, CONSTANT_INT_CODE = 1, CONSTANT_OOP_CODE = 2,
99114
CONSTANT_LONG_CODE = 3, CONSTANT_DOUBLE_CODE = 4,
100115
OBJECT_CODE = 5, OBJECT_ID_CODE = 6,
101-
AUTO_BOX_OBJECT_CODE = 7, MARKER_CODE = 8 };
116+
AUTO_BOX_OBJECT_CODE = 7, MARKER_CODE = 8,
117+
OBJECT_MERGE_CODE = 9 };
102118

103119
ScopeValue* ScopeValue::read_from(DebugInfoReadStream* stream) {
104120
ScopeValue* result = nullptr;
@@ -110,6 +126,7 @@ ScopeValue* ScopeValue::read_from(DebugInfoReadStream* stream) {
110126
case CONSTANT_DOUBLE_CODE: result = new ConstantDoubleValue(stream); break;
111127
case OBJECT_CODE: result = stream->read_object_value(false /*is_auto_box*/); break;
112128
case AUTO_BOX_OBJECT_CODE: result = stream->read_object_value(true /*is_auto_box*/); break;
129+
case OBJECT_MERGE_CODE: result = stream->read_object_merge_value(); break;
113130
case OBJECT_ID_CODE: result = stream->get_cached_object(); break;
114131
case MARKER_CODE: result = new MarkerValue(); break;
115132
default: ShouldNotReachHere();
@@ -149,6 +166,7 @@ void ObjectValue::set_value(oop value) {
149166
}
150167

151168
void ObjectValue::read_object(DebugInfoReadStream* stream) {
169+
_is_root = stream->read_bool();
152170
_klass = read_from(stream);
153171
assert(_klass->is_constant_oop(), "should be constant java mirror oop");
154172
int length = stream->read_int();
@@ -166,6 +184,7 @@ void ObjectValue::write_on(DebugInfoWriteStream* stream) {
166184
set_visited(true);
167185
stream->write_int(is_auto_box() ? AUTO_BOX_OBJECT_CODE : OBJECT_CODE);
168186
stream->write_int(_id);
187+
stream->write_bool(_is_root);
169188
_klass->write_on(stream);
170189
int length = _field_values.length();
171190
stream->write_int(length);
@@ -176,21 +195,106 @@ void ObjectValue::write_on(DebugInfoWriteStream* stream) {
176195
}
177196

178197
void ObjectValue::print_on(outputStream* st) const {
179-
st->print("%s[%d]", is_auto_box() ? "box_obj" : "obj", _id);
198+
st->print("%s[%d]", is_auto_box() ? "box_obj" : is_object_merge() ? "merge_obj" : "obj", _id);
180199
}
181200

182201
void ObjectValue::print_fields_on(outputStream* st) const {
183202
#ifndef PRODUCT
184-
if (_field_values.length() > 0) {
185-
_field_values.at(0)->print_on(st);
186-
}
187-
for (int i = 1; i < _field_values.length(); i++) {
188-
st->print(", ");
189-
_field_values.at(i)->print_on(st);
203+
if (is_object_merge()) {
204+
ObjectMergeValue* omv = (ObjectMergeValue*)this;
205+
st->print("selector=\"");
206+
omv->selector()->print_on(st);
207+
st->print("\"");
208+
ScopeValue* merge_pointer = omv->merge_pointer();
209+
if (!(merge_pointer->is_object() && merge_pointer->as_ObjectValue()->value()() == nullptr) &&
210+
!(merge_pointer->is_constant_oop() && merge_pointer->as_ConstantOopReadValue()->value()() == nullptr)) {
211+
st->print(", merge_pointer=\"");
212+
merge_pointer->print_on(st);
213+
st->print("\"");
214+
}
215+
GrowableArray<ScopeValue*>* possible_objects = omv->possible_objects();
216+
st->print(", candidate_objs=[%d", possible_objects->at(0)->as_ObjectValue()->id());
217+
int ncandidates = possible_objects->length();
218+
for (int i = 1; i < ncandidates; i++) {
219+
st->print(", %d", possible_objects->at(i)->as_ObjectValue()->id());
220+
}
221+
st->print("]");
222+
} else {
223+
st->print("\n Fields: ");
224+
if (_field_values.length() > 0) {
225+
_field_values.at(0)->print_on(st);
226+
}
227+
for (int i = 1; i < _field_values.length(); i++) {
228+
st->print(", ");
229+
_field_values.at(i)->print_on(st);
230+
}
190231
}
191232
#endif
192233
}
193234

235+
236+
// ObjectMergeValue
237+
238+
// Returns the ObjectValue that should be used for the local that this
239+
// ObjectMergeValue represents. ObjectMergeValue represents allocation
240+
// merges in C2. This method will select which path the allocation merge
241+
// took during execution of the Trap that triggered the rematerialization
242+
// of the object.
243+
ObjectValue* ObjectMergeValue::select(frame& fr, RegisterMap& reg_map) {
244+
StackValue* sv_selector = StackValue::create_stack_value(&fr, &reg_map, _selector);
245+
jint selector = sv_selector->get_int();
246+
247+
// If the selector is '-1' it means that execution followed the path
248+
// where no scalar replacement happened.
249+
// Otherwise, it is the index in _possible_objects array that holds
250+
// the description of the scalar replaced object.
251+
if (selector == -1) {
252+
StackValue* sv_merge_pointer = StackValue::create_stack_value(&fr, &reg_map, _merge_pointer);
253+
_selected = new ObjectValue(id());
254+
255+
// Retrieve the pointer to the real object and use it as if we had
256+
// allocated it during the deoptimization
257+
_selected->set_value(sv_merge_pointer->get_obj()());
258+
259+
// No need to rematerialize
260+
return nullptr;
261+
} else {
262+
assert(selector < _possible_objects.length(), "sanity");
263+
_selected = (ObjectValue*) _possible_objects.at(selector);
264+
return _selected;
265+
}
266+
}
267+
268+
void ObjectMergeValue::read_object(DebugInfoReadStream* stream) {
269+
_selector = read_from(stream);
270+
_merge_pointer = read_from(stream);
271+
int ncandidates = stream->read_int();
272+
for (int i = 0; i < ncandidates; i++) {
273+
ScopeValue* result = read_from(stream);
274+
assert(result->is_object(), "Candidate is not an object!");
275+
ObjectValue* obj = result->as_ObjectValue();
276+
_possible_objects.append(obj);
277+
}
278+
}
279+
280+
void ObjectMergeValue::write_on(DebugInfoWriteStream* stream) {
281+
if (is_visited()) {
282+
stream->write_int(OBJECT_ID_CODE);
283+
stream->write_int(_id);
284+
} else {
285+
set_visited(true);
286+
stream->write_int(OBJECT_MERGE_CODE);
287+
stream->write_int(_id);
288+
_selector->write_on(stream);
289+
_merge_pointer->write_on(stream);
290+
int ncandidates = _possible_objects.length();
291+
stream->write_int(ncandidates);
292+
for (int i = 0; i < ncandidates; i++) {
293+
_possible_objects.at(i)->as_ObjectValue()->write_on(stream);
294+
}
295+
}
296+
}
297+
194298
// ConstantIntValue
195299

196300
ConstantIntValue::ConstantIntValue(DebugInfoReadStream* stream) {

src/hotspot/share/code/debugInfo.hpp

Lines changed: 89 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,14 @@ class ConstantOopReadValue;
4444
class ConstantOopWriteValue;
4545
class LocationValue;
4646
class ObjectValue;
47+
class ObjectMergeValue;
4748

4849
class ScopeValue: public AnyObj {
4950
public:
5051
// Testers
5152
virtual bool is_location() const { return false; }
5253
virtual bool is_object() const { return false; }
54+
virtual bool is_object_merge() const { return false; }
5355
virtual bool is_auto_box() const { return false; }
5456
virtual bool is_marker() const { return false; }
5557
virtual bool is_constant_int() const { return false; }
@@ -73,6 +75,11 @@ class ScopeValue: public AnyObj {
7375
return (ObjectValue*)this;
7476
}
7577

78+
ObjectMergeValue* as_ObjectMergeValue() {
79+
assert(is_object_merge(), "must be");
80+
return (ObjectMergeValue*)this;
81+
}
82+
7683
LocationValue* as_LocationValue() {
7784
assert(is_location(), "must be");
7885
return (LocationValue*)this;
@@ -126,13 +133,18 @@ class ObjectValue: public ScopeValue {
126133
GrowableArray<ScopeValue*> _field_values;
127134
Handle _value;
128135
bool _visited;
136+
bool _is_root; // Will be true if this object is referred to
137+
// as a local/expression/monitor in the JVMs.
138+
// Otherwise false, meaning it's just a candidate
139+
// in an object allocation merge.
129140
public:
130141
ObjectValue(int id, ScopeValue* klass)
131142
: _id(id)
132143
, _klass(klass)
133144
, _field_values()
134145
, _value()
135-
, _visited(false) {
146+
, _visited(false)
147+
, _is_root(true) {
136148
assert(klass->is_constant_oop(), "should be constant java mirror oop");
137149
}
138150

@@ -141,20 +153,24 @@ class ObjectValue: public ScopeValue {
141153
, _klass(nullptr)
142154
, _field_values()
143155
, _value()
144-
, _visited(false) {}
156+
, _visited(false)
157+
, _is_root(true) {}
145158

146159
// Accessors
147-
bool is_object() const { return true; }
148-
int id() const { return _id; }
149-
ScopeValue* klass() const { return _klass; }
150-
GrowableArray<ScopeValue*>* field_values() { return &_field_values; }
151-
ScopeValue* field_at(int i) const { return _field_values.at(i); }
152-
int field_size() { return _field_values.length(); }
153-
Handle value() const { return _value; }
154-
bool is_visited() const { return _visited; }
155-
156-
void set_value(oop value);
157-
void set_visited(bool visited) { _visited = visited; }
160+
bool is_object() const { return true; }
161+
int id() const { return _id; }
162+
virtual ScopeValue* klass() const { return _klass; }
163+
virtual GrowableArray<ScopeValue*>* field_values() { return &_field_values; }
164+
virtual ScopeValue* field_at(int i) const { return _field_values.at(i); }
165+
virtual int field_size() { return _field_values.length(); }
166+
virtual Handle value() const { return _value; }
167+
bool is_visited() const { return _visited; }
168+
bool is_root() const { return _is_root; }
169+
170+
void set_id(int id) { _id = id; }
171+
virtual void set_value(oop value);
172+
void set_visited(bool visited) { _visited = visited; }
173+
void set_root(bool root) { _is_root = root; }
158174

159175
// Serialization of debugging information
160176
void read_object(DebugInfoReadStream* stream);
@@ -165,6 +181,65 @@ class ObjectValue: public ScopeValue {
165181
void print_fields_on(outputStream* st) const;
166182
};
167183

184+
// An ObjectMergeValue describes objects that were inputs to a Phi in C2 and at
185+
// least one of them was scalar replaced.
186+
// '_selector' is an integer value that will be '-1' if during the execution of
187+
// the C2 compiled code the path taken was that of the Phi input that was NOT
188+
// scalar replaced. In that case '_merge_pointer' is a pointer to an already
189+
// allocated object. If '_selector' is not '-1' then it should be the index of
190+
// an object in '_possible_objects'. That object is an ObjectValue describing an
191+
// object that was scalar replaced.
192+
193+
class ObjectMergeValue: public ObjectValue {
194+
protected:
195+
ScopeValue* _selector;
196+
ScopeValue* _merge_pointer;
197+
GrowableArray<ScopeValue*> _possible_objects;
198+
199+
// This holds the ObjectValue that should be used in place of this
200+
// ObjectMergeValue. I.e., it's the ScopeValue from _possible_objects that was
201+
// selected by 'select()' or is a on-the-fly created ScopeValue representing
202+
// the _merge_pointer if _selector is -1.
203+
//
204+
// We need to keep this reference around because there will be entries in
205+
// ScopeDesc that reference this ObjectMergeValue directly. After
206+
// rematerialization ObjectMergeValue will be just a wrapper for the
207+
// Objectvalue pointed by _selected.
208+
ObjectValue* _selected;
209+
public:
210+
ObjectMergeValue(int id, ScopeValue* merge_pointer, ScopeValue* selector)
211+
: ObjectValue(id)
212+
, _selector(selector)
213+
, _merge_pointer(merge_pointer)
214+
, _possible_objects()
215+
, _selected(nullptr) {}
216+
217+
ObjectMergeValue(int id)
218+
: ObjectValue(id)
219+
, _selector(nullptr)
220+
, _merge_pointer(nullptr)
221+
, _possible_objects()
222+
, _selected(nullptr) {}
223+
224+
bool is_object_merge() const { return true; }
225+
ScopeValue* selector() const { return _selector; }
226+
ScopeValue* merge_pointer() const { return _merge_pointer; }
227+
GrowableArray<ScopeValue*>* possible_objects() { return &_possible_objects; }
228+
ObjectValue* select(frame& fr, RegisterMap& reg_map) ;
229+
230+
ScopeValue* klass() const { ShouldNotReachHere(); return nullptr; }
231+
GrowableArray<ScopeValue*>* field_values() { ShouldNotReachHere(); return nullptr; }
232+
ScopeValue* field_at(int i) const { ShouldNotReachHere(); return nullptr; }
233+
int field_size() { ShouldNotReachHere(); return -1; }
234+
235+
Handle value() const { assert(_selected != nullptr, "Should call select() first."); return _selected->value(); }
236+
void set_value(oop value) { assert(_selected != nullptr, "Should call select() first."); _selected->set_value(value); }
237+
238+
// Serialization of debugging information
239+
void read_object(DebugInfoReadStream* stream);
240+
void write_on(DebugInfoWriteStream* stream);
241+
};
242+
168243
class AutoBoxObjectValue : public ObjectValue {
169244
bool _cached;
170245
public:
@@ -316,6 +391,7 @@ class DebugInfoReadStream : public CompressedReadStream {
316391
return o;
317392
}
318393
ScopeValue* read_object_value(bool is_auto_box);
394+
ScopeValue* read_object_merge_value();
319395
ScopeValue* get_cached_object();
320396
// BCI encoding is mostly unsigned, but -1 is a distinguished value
321397
int read_bci() { return read_int() + InvocationEntryBci; }

src/hotspot/share/code/scopeDesc.cpp

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,6 @@ GrowableArray<ScopeValue*>* ScopeDesc::decode_object_values(int decode_offset) {
114114
// object's fields could reference it (OBJECT_ID_CODE).
115115
(void)ScopeValue::read_from(stream);
116116
}
117-
assert(result->length() == length, "inconsistent debug information");
118117
return result;
119118
}
120119

@@ -130,6 +129,38 @@ GrowableArray<MonitorValue*>* ScopeDesc::decode_monitor_values(int decode_offset
130129
return result;
131130
}
132131

132+
GrowableArray<ScopeValue*>* ScopeDesc::objects_to_rematerialize(frame& frm, RegisterMap& map) {
133+
if (_objects == nullptr) {
134+
return nullptr;
135+
}
136+
137+
GrowableArray<ScopeValue*>* result = new GrowableArray<ScopeValue*>();
138+
for (int i = 0; i < _objects->length(); i++) {
139+
assert(_objects->at(i)->is_object(), "invalid debug information");
140+
ObjectValue* sv = _objects->at(i)->as_ObjectValue();
141+
142+
// If the object is not referenced in current JVM state, then it's only
143+
// a candidate in an ObjectMergeValue, we don't need to rematerialize it
144+
// unless when/if it's returned by 'select()' below.
145+
if (!sv->is_root()) {
146+
continue;
147+
}
148+
149+
if (sv->is_object_merge()) {
150+
sv = sv->as_ObjectMergeValue()->select(frm, map);
151+
// If select() returns nullptr, then the object doesn't need to be
152+
// rematerialized.
153+
if (sv == nullptr) {
154+
continue;
155+
}
156+
}
157+
158+
result->append_if_missing(sv);
159+
}
160+
161+
return result;
162+
}
163+
133164
DebugInfoReadStream* ScopeDesc::stream_at(int decode_offset) const {
134165
return new DebugInfoReadStream(_code, decode_offset, _objects);
135166
}
@@ -238,8 +269,12 @@ void ScopeDesc::print_on(outputStream* st, PcDesc* pd) const {
238269
st->print_cr(" Objects");
239270
for (int i = 0; i < _objects->length(); i++) {
240271
ObjectValue* sv = (ObjectValue*) _objects->at(i);
241-
st->print(" - %d: ", sv->id());
242-
st->print("%s ", java_lang_Class::as_Klass(sv->klass()->as_ConstantOopReadValue()->value()())->external_name());
272+
st->print(" - %d: %c ", i, sv->is_root() ? 'R' : ' ');
273+
sv->print_on(st);
274+
st->print(", ");
275+
if (!sv->is_object_merge()) {
276+
st->print("%s", java_lang_Class::as_Klass(sv->klass()->as_ConstantOopReadValue()->value()())->external_name());
277+
}
243278
sv->print_fields_on(st);
244279
st->cr();
245280
}

0 commit comments

Comments
 (0)