Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
8280473: CI: Support unresolved JVM_CONSTANT_Dynamic constant pool en…
…tries

Reviewed-by: dlong, redestad, neliasso
  • Loading branch information
Vladimir Ivanov committed Feb 14, 2022
1 parent f07b816 commit 88fc3bf
Show file tree
Hide file tree
Showing 12 changed files with 417 additions and 102 deletions.
65 changes: 40 additions & 25 deletions src/hotspot/share/c1/c1_GraphBuilder.cpp
Expand Up @@ -34,6 +34,7 @@
#include "ci/ciMemberName.hpp"
#include "ci/ciSymbols.hpp"
#include "ci/ciUtilities.inline.hpp"
#include "classfile/javaClasses.hpp"
#include "compiler/compilationPolicy.hpp"
#include "compiler/compileBroker.hpp"
#include "compiler/compilerEvent.hpp"
Expand Down Expand Up @@ -916,53 +917,67 @@ void GraphBuilder::ScopeData::incr_num_returns() {

void GraphBuilder::load_constant() {
ciConstant con = stream()->get_constant();
if (con.basic_type() == T_ILLEGAL) {
// FIXME: an unresolved Dynamic constant can get here,
// and that should not terminate the whole compilation.
BAILOUT("could not resolve a constant");
} else {
if (con.is_valid()) {
ValueType* t = illegalType;
ValueStack* patch_state = NULL;
switch (con.basic_type()) {
case T_BOOLEAN: t = new IntConstant (con.as_boolean()); break;
case T_BYTE : t = new IntConstant (con.as_byte ()); break;
case T_CHAR : t = new IntConstant (con.as_char ()); break;
case T_SHORT : t = new IntConstant (con.as_short ()); break;
case T_INT : t = new IntConstant (con.as_int ()); break;
case T_LONG : t = new LongConstant (con.as_long ()); break;
case T_FLOAT : t = new FloatConstant (con.as_float ()); break;
case T_DOUBLE : t = new DoubleConstant (con.as_double ()); break;
case T_ARRAY : t = new ArrayConstant (con.as_object ()->as_array ()); break;
case T_OBJECT :
{
case T_BOOLEAN: t = new IntConstant (con.as_boolean()); break;
case T_BYTE : t = new IntConstant (con.as_byte ()); break;
case T_CHAR : t = new IntConstant (con.as_char ()); break;
case T_SHORT : t = new IntConstant (con.as_short ()); break;
case T_INT : t = new IntConstant (con.as_int ()); break;
case T_LONG : t = new LongConstant (con.as_long ()); break;
case T_FLOAT : t = new FloatConstant (con.as_float ()); break;
case T_DOUBLE : t = new DoubleConstant(con.as_double ()); break;
case T_ARRAY : // fall-through
case T_OBJECT : {
ciObject* obj = con.as_object();
if (!obj->is_loaded()
|| (PatchALot && obj->klass() != ciEnv::current()->String_klass())) {
// A Class, MethodType, MethodHandle, or String.
// Unloaded condy nodes show up as T_ILLEGAL, above.
if (!obj->is_loaded() || (PatchALot && (obj->is_null_object() || obj->klass() != ciEnv::current()->String_klass()))) {
// A Class, MethodType, MethodHandle, Dynamic, or String.
patch_state = copy_state_before();
t = new ObjectConstant(obj);
} else {
// Might be a Class, MethodType, MethodHandle, or Dynamic constant
// result, which might turn out to be an array.
if (obj->is_null_object())
if (obj->is_null_object()) {
t = objectNull;
else if (obj->is_array())
} else if (obj->is_array()) {
t = new ArrayConstant(obj->as_array());
else
} else {
t = new InstanceConstant(obj->as_instance());
}
}
break;
}
default : ShouldNotReachHere();
}
default: ShouldNotReachHere();
}
Value x;
if (patch_state != NULL) {
x = new Constant(t, patch_state);
} else {
x = new Constant(t);
}

// Unbox the value at runtime, if needed.
// ConstantDynamic entry can be of a primitive type, but it is cached in boxed form.
if (patch_state != NULL) {
int index = stream()->get_constant_pool_index();
BasicType type = stream()->get_basic_type_for_constant_at(index);
if (is_java_primitive(type)) {
ciInstanceKlass* box_klass = ciEnv::current()->get_box_klass_for_primitive_type(type);
assert(box_klass->is_loaded(), "sanity");
int offset = java_lang_boxing_object::value_offset(type);
ciField* value_field = box_klass->get_field_by_offset(offset, false /*is_static*/);
x = new LoadField(append(x), offset, value_field, false /*is_static*/, patch_state, false /*needs_patching*/);
t = as_ValueType(type);
} else {
assert(is_reference_type(type), "not a reference: %s", type2name(type));
}
}

push(t, append(x));
} else {
BAILOUT("could not resolve a constant");
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/hotspot/share/c1/c1_InstructionPrinter.cpp
Expand Up @@ -262,7 +262,7 @@ void InstructionPrinter::print_inline_level(BlockBegin* block) {


void InstructionPrinter::print_unsafe_op(UnsafeOp* op, const char* name) {
output()->print("%s", name);
output()->print("%s(", name);
print_value(op->object());
output()->print(", ");
print_value(op->offset());
Expand Down
1 change: 1 addition & 0 deletions src/hotspot/share/c1/c1_LIRAssembler.cpp
Expand Up @@ -76,6 +76,7 @@ void LIR_Assembler::patching_epilog(PatchingStub* patch, LIR_PatchCode patch_cod
case Bytecodes::_getstatic:
case Bytecodes::_ldc:
case Bytecodes::_ldc_w:
case Bytecodes::_ldc2_w:
break;
default:
ShouldNotReachHere();
Expand Down
2 changes: 1 addition & 1 deletion src/hotspot/share/c1/c1_Runtime1.cpp
Expand Up @@ -1009,6 +1009,7 @@ JRT_ENTRY(void, Runtime1::patch_code(JavaThread* current, Runtime1::StubID stub_
break;
case Bytecodes::_ldc:
case Bytecodes::_ldc_w:
case Bytecodes::_ldc2_w:
{
Bytecode_loadconstant cc(caller_method, bci);
oop m = cc.resolve_constant(CHECK);
Expand Down Expand Up @@ -1153,7 +1154,6 @@ JRT_ENTRY(void, Runtime1::patch_code(JavaThread* current, Runtime1::StubID stub_
assert(load_klass != NULL, "klass not set");
n_copy->set_data((intx) (load_klass));
} else {
assert(mirror() != NULL, "klass not set");
// Don't need a G1 pre-barrier here since we assert above that data isn't an oop.
n_copy->set_data(cast_from_oop<intx>(mirror()));
}
Expand Down
141 changes: 87 additions & 54 deletions src/hotspot/share/ci/ciEnv.cpp
Expand Up @@ -392,6 +392,23 @@ ciInstance* ciEnv::get_or_create_exception(jobject& handle, Symbol* name) {
return obj == NULL? NULL: get_object(obj)->as_instance();
}

ciInstanceKlass* ciEnv::get_box_klass_for_primitive_type(BasicType type) {
switch (type) {
case T_BOOLEAN: return Boolean_klass();
case T_BYTE : return Byte_klass();
case T_CHAR : return Character_klass();
case T_SHORT : return Short_klass();
case T_INT : return Integer_klass();
case T_LONG : return Long_klass();
case T_FLOAT : return Float_klass();
case T_DOUBLE : return Double_klass();

default:
assert(false, "not a primitive: %s", type2name(type));
return NULL;
}
}

ciInstance* ciEnv::ArrayIndexOutOfBoundsException_instance() {
if (_ArrayIndexOutOfBoundsException_instance == NULL) {
_ArrayIndexOutOfBoundsException_instance
Expand Down Expand Up @@ -650,54 +667,75 @@ ciKlass* ciEnv::get_klass_by_index(const constantPoolHandle& cpool,
GUARDED_VM_ENTRY(return get_klass_by_index_impl(cpool, index, is_accessible, accessor);)
}

// ------------------------------------------------------------------
// ciEnv::unbox_primitive_value
//
// Unbox a primitive and return it as a ciConstant.
ciConstant ciEnv::unbox_primitive_value(ciObject* cibox, BasicType expected_bt) {
jvalue value;
BasicType bt = java_lang_boxing_object::get_value(cibox->get_oop(), &value);
if (bt != expected_bt && expected_bt != T_ILLEGAL) {
assert(false, "type mismatch: %s vs %s", type2name(expected_bt), cibox->klass()->name()->as_klass_external_name());
return ciConstant();
}
switch (bt) {
case T_BOOLEAN: return ciConstant(bt, value.z);
case T_BYTE: return ciConstant(bt, value.b);
case T_SHORT: return ciConstant(bt, value.s);
case T_CHAR: return ciConstant(bt, value.c);
case T_INT: return ciConstant(bt, value.i);
case T_LONG: return ciConstant(value.j);
case T_FLOAT: return ciConstant(value.f);
case T_DOUBLE: return ciConstant(value.d);

default:
assert(false, "not a primitive type: %s", type2name(bt));
return ciConstant();
}
}

// ------------------------------------------------------------------
// ciEnv::get_resolved_constant
//
ciConstant ciEnv::get_resolved_constant(const constantPoolHandle& cpool, int obj_index) {
assert(obj_index >= 0, "");
oop obj = cpool->resolved_references()->obj_at(obj_index);
if (obj == NULL) {
// Unresolved constant. It is resolved when the corresponding slot contains a non-null reference.
// Null constant is represented as a sentinel (non-null) value.
return ciConstant();
} else if (obj == Universe::the_null_sentinel()) {
return ciConstant(T_OBJECT, get_object(NULL));
} else {
ciObject* ciobj = get_object(obj);
if (ciobj->is_array()) {
return ciConstant(T_ARRAY, ciobj);
} else {
int cp_index = cpool->object_to_cp_index(obj_index);
BasicType bt = cpool->basic_type_for_constant_at(cp_index);
if (is_java_primitive(bt)) {
assert(cpool->tag_at(cp_index).is_dynamic_constant(), "sanity");
return unbox_primitive_value(ciobj, bt);
} else {
assert(ciobj->is_instance(), "should be an instance");
return ciConstant(T_OBJECT, ciobj);
}
}
}
}

// ------------------------------------------------------------------
// ciEnv::get_constant_by_index_impl
//
// Implementation of get_constant_by_index().
ciConstant ciEnv::get_constant_by_index_impl(const constantPoolHandle& cpool,
int pool_index, int cache_index,
int index, int obj_index,
ciInstanceKlass* accessor) {
bool ignore_will_link;
int index = pool_index;
if (cache_index >= 0) {
assert(index < 0, "only one kind of index at a time");
index = cpool->object_to_cp_index(cache_index);
oop obj = cpool->resolved_references()->obj_at(cache_index);
if (obj != NULL) {
if (obj == Universe::the_null_sentinel()) {
return ciConstant(T_OBJECT, get_object(NULL));
}
BasicType bt = T_OBJECT;
if (cpool->tag_at(index).is_dynamic_constant()) {
bt = Signature::basic_type(cpool->uncached_signature_ref_at(index));
}
if (!is_reference_type(bt)) {
// we have to unbox the primitive value
if (!is_java_primitive(bt)) {
return ciConstant();
}
jvalue value;
BasicType bt2 = java_lang_boxing_object::get_value(obj, &value);
assert(bt2 == bt, "");
switch (bt2) {
case T_DOUBLE: return ciConstant(value.d);
case T_FLOAT: return ciConstant(value.f);
case T_LONG: return ciConstant(value.j);
case T_INT: return ciConstant(bt2, value.i);
case T_SHORT: return ciConstant(bt2, value.s);
case T_BYTE: return ciConstant(bt2, value.b);
case T_CHAR: return ciConstant(bt2, value.c);
case T_BOOLEAN: return ciConstant(bt2, value.z);
default: return ciConstant();
}
}
ciObject* ciobj = get_object(obj);
if (ciobj->is_array()) {
return ciConstant(T_ARRAY, ciobj);
} else {
assert(ciobj->is_instance(), "should be an instance");
return ciConstant(T_OBJECT, ciobj);
}
if (obj_index >= 0) {
ciConstant con = get_resolved_constant(cpool, obj_index);
if (con.is_valid()) {
return con;
}
}
constantTag tag = cpool->tag_at(index);
Expand All @@ -711,35 +749,29 @@ ciConstant ciEnv::get_constant_by_index_impl(const constantPoolHandle& cpool,
return ciConstant((jdouble)cpool->double_at(index));
} else if (tag.is_string()) {
EXCEPTION_CONTEXT;
oop string = NULL;
assert(cache_index >= 0, "should have a cache index");
string = cpool->string_at(index, cache_index, THREAD);
assert(obj_index >= 0, "should have an object index");
oop string = cpool->string_at(index, obj_index, THREAD);
if (HAS_PENDING_EXCEPTION) {
CLEAR_PENDING_EXCEPTION;
record_out_of_memory_failure();
return ciConstant();
}
ciObject* constant = get_object(string);
if (constant->is_array()) {
return ciConstant(T_ARRAY, constant);
} else {
assert (constant->is_instance(), "must be an instance, or not? ");
return ciConstant(T_OBJECT, constant);
}
ciInstance* constant = get_object(string)->as_instance();
return ciConstant(T_OBJECT, constant);
} else if (tag.is_unresolved_klass_in_error()) {
return ciConstant(T_OBJECT, get_unloaded_klass_mirror(NULL));
} else if (tag.is_klass() || tag.is_unresolved_klass()) {
ciKlass* klass = get_klass_by_index_impl(cpool, index, ignore_will_link, accessor);
assert (klass->is_instance_klass() || klass->is_array_klass(),
"must be an instance or array klass ");
return ciConstant(T_OBJECT, klass->java_mirror());
} else if (tag.is_method_type() || tag.is_method_type_in_error()) {
// must execute Java code to link this CP entry into cache[i].f1
assert(obj_index >= 0, "should have an object index");
ciSymbol* signature = get_symbol(cpool->method_type_signature_at(index));
ciObject* ciobj = get_unloaded_method_type_constant(signature);
return ciConstant(T_OBJECT, ciobj);
} else if (tag.is_method_handle() || tag.is_method_handle_in_error()) {
// must execute Java code to link this CP entry into cache[i].f1
assert(obj_index >= 0, "should have an object index");
int ref_kind = cpool->method_handle_ref_kind_at(index);
int callee_index = cpool->method_handle_klass_index_at(index);
ciKlass* callee = get_klass_by_index_impl(cpool, callee_index, ignore_will_link, accessor);
Expand All @@ -748,7 +780,8 @@ ciConstant ciEnv::get_constant_by_index_impl(const constantPoolHandle& cpool,
ciObject* ciobj = get_unloaded_method_handle_constant(callee, name, signature, ref_kind);
return ciConstant(T_OBJECT, ciobj);
} else if (tag.is_dynamic_constant() || tag.is_dynamic_constant_in_error()) {
return ciConstant(); // not supported
assert(obj_index >= 0, "should have an object index");
return ciConstant(T_OBJECT, unloaded_ciinstance()); // unresolved dynamic constant
} else {
assert(false, "unknown tag: %d (%s)", tag.value(), tag.internal_name());
return ciConstant();
Expand Down
5 changes: 5 additions & 0 deletions src/hotspot/share/ci/ciEnv.hpp
Expand Up @@ -165,6 +165,9 @@ class ciEnv : StackObj {
Bytecodes::Code bc,
constantTag tag);

ciConstant unbox_primitive_value(ciObject* cibox, BasicType expected_bt = T_ILLEGAL);
ciConstant get_resolved_constant(const constantPoolHandle& cpool, int obj_index);

// Get a ciObject from the object factory. Ensures uniqueness
// of ciObjects.
ciObject* get_object(oop o) {
Expand Down Expand Up @@ -429,6 +432,8 @@ class ciEnv : StackObj {
}
ciInstance* unloaded_ciinstance();

ciInstanceKlass* get_box_klass_for_primitive_type(BasicType type);

// Note: To find a class from its name string, use ciSymbol::make,
// but consider adding to vmSymbols.hpp instead.

Expand Down
8 changes: 4 additions & 4 deletions src/hotspot/share/ci/ciObjectFactory.cpp
Expand Up @@ -555,7 +555,7 @@ ciInstance* ciObjectFactory::get_unloaded_instance(ciInstanceKlass* instance_kla
// Get a ciInstance representing an unresolved klass mirror.
//
// Currently, this ignores the parameters and returns a unique unloaded instance.
ciInstance* ciObjectFactory::get_unloaded_klass_mirror(ciKlass* type) {
ciInstance* ciObjectFactory::get_unloaded_klass_mirror(ciKlass* type) {
assert(ciEnv::_Class_klass != NULL, "");
return get_unloaded_instance(ciEnv::_Class_klass->as_instance_klass());
}
Expand All @@ -570,7 +570,7 @@ ciInstance* ciObjectFactory::get_unloaded_method_handle_constant(ciKlass* holde
ciSymbol* name,
ciSymbol* signature,
int ref_kind) {
if (ciEnv::_MethodHandle_klass == NULL) return NULL;
assert(ciEnv::_MethodHandle_klass != NULL, "");
return get_unloaded_instance(ciEnv::_MethodHandle_klass->as_instance_klass());
}

Expand All @@ -581,12 +581,12 @@ ciInstance* ciObjectFactory::get_unloaded_method_handle_constant(ciKlass* holde
//
// Currently, this ignores the parameters and returns a unique unloaded instance.
ciInstance* ciObjectFactory::get_unloaded_method_type_constant(ciSymbol* signature) {
if (ciEnv::_MethodType_klass == NULL) return NULL;
assert(ciEnv::_MethodType_klass != NULL, "");
return get_unloaded_instance(ciEnv::_MethodType_klass->as_instance_klass());
}

ciInstance* ciObjectFactory::get_unloaded_object_constant() {
if (ciEnv::_Object_klass == NULL) return NULL;
assert(ciEnv::_Object_klass != NULL, "");
return get_unloaded_instance(ciEnv::_Object_klass->as_instance_klass());
}

Expand Down

1 comment on commit 88fc3bf

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.