Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8267846: [lworld] JIT support for the L/Q model (step 1) #431

Closed
wants to merge 3 commits into from
Closed
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -513,10 +513,8 @@ void LIR_Assembler::return_op(LIR_Opr result, C1SafepointPollStub* code_stub) {
assert(result->is_illegal() || !result->is_single_cpu() || result->as_register() == r0, "word returns are in r0,");

ciMethod* method = compilation()->method();

ciType* return_type = method->return_type();
if (InlineTypeReturnedAsFields && return_type->is_inlinetype()) {
ciInlineKlass* vk = return_type->as_inline_klass();
if (InlineTypeReturnedAsFields && method->signature()->returns_null_free_inline_type()) {
ciInlineKlass* vk = method->return_type()->as_inline_klass();
if (vk->can_be_returned_as_fields()) {
address unpack_handler = vk->unpack_handler();
assert(unpack_handler != NULL, "must be");
@@ -531,9 +531,8 @@ void LIR_Assembler::return_op(LIR_Opr result, C1SafepointPollStub* code_stub) {
}

ciMethod* method = compilation()->method();
ciType* return_type = method->return_type();
if (InlineTypeReturnedAsFields && return_type->is_inlinetype() && return_type->is_null_free()) {
ciInlineKlass* vk = return_type->as_inline_klass();
if (InlineTypeReturnedAsFields && method->signature()->returns_null_free_inline_type()) {
ciInlineKlass* vk = method->return_type()->as_inline_klass();
if (vk->can_be_returned_as_fields()) {
#ifndef _LP64
Unimplemented();
@@ -662,15 +662,14 @@ void Canonicalizer::do_CheckCast (CheckCast* x) {
klass->as_instance_klass()->is_interface();
// Interface casts can't be statically optimized away since verifier doesn't
// enforce interface types in bytecode.
if (!is_interface && klass->is_subtype_of(x->klass())) {
if (!is_interface && klass->is_subtype_of(x->klass()) && (!x->is_null_free() || obj->is_null_free())) {
assert(!x->klass()->is_inlinetype() || x->klass() == klass, "Inline klasses can't have subtypes");
set_canonical(obj);
return;
}
}
// checkcast of null returns null for non-inline klasses
if (!x->klass()->is_inlinetype() && x->is_null_free()
&& obj->as_Constant() && obj->type()->as_ObjectType()->constant_value()->is_null_object()) {
// checkcast of null returns null for non null-free klasses
if (!x->is_null_free() && obj->is_null_obj()) {
set_canonical(obj);
}
}
@@ -684,7 +683,7 @@ void Canonicalizer::do_InstanceOf (InstanceOf* x) {
return;
}
// instanceof null returns false
if (obj->as_Constant() && obj->type()->as_ObjectType()->constant_value()->is_null_object()) {
if (obj->as_Constant() && obj->is_null_obj()) {
set_constant(0);
}
}
@@ -1844,9 +1844,9 @@ void GraphBuilder::access_field(Bytecodes::Code code) {
assert(!field->is_stable() || !field_value.is_null_or_zero(),
"stable static w/ default value shouldn't be a constant");
constant = make_constant(field_value, field);
} else if (field_type == T_INLINE_TYPE && field->type()->is_null_free() && field->type()->unwrap()->as_inline_klass()->is_empty()) {
} else if (field->is_null_free() && field->type()->is_loaded() && field->type()->as_inline_klass()->is_empty()) {
// Loading from a field of an empty inline type. Just return the default instance.
constant = new Constant(new InstanceConstant(field->type()->unwrap()->as_inline_klass()->default_instance()));
constant = new Constant(new InstanceConstant(field->type()->as_inline_klass()->default_instance()));
}
if (constant != NULL) {
push(type, append(constant));
@@ -1869,7 +1869,7 @@ void GraphBuilder::access_field(Bytecodes::Code code) {
Value mask = append(new Constant(new IntConstant(1)));
val = append(new LogicOp(Bytecodes::_iand, val, mask));
}
if (field_type == T_INLINE_TYPE && field->type()->unwrap()->as_inline_klass()->is_empty()) {
if (field->is_null_free() && field->type()->is_loaded() && field->type()->as_inline_klass()->is_empty()) {
// Storing to a field of an empty inline type. Ignore.
break;
}
@@ -1887,18 +1887,18 @@ void GraphBuilder::access_field(Bytecodes::Code code) {
if (!has_pending_field_access() && !has_pending_load_indexed()) {
obj = apop();
ObjectType* obj_type = obj->type()->as_ObjectType();
if (field_type == T_INLINE_TYPE && field->type()->is_null_free() && field->type()->unwrap()->as_inline_klass()->is_empty()) {
if (field->is_null_free() && field->type()->is_loaded() && field->type()->as_inline_klass()->is_empty()) {
// Loading from a field of an empty inline type. Just return the default instance.
null_check(obj);
constant = new Constant(new InstanceConstant(field->type()->unwrap()->as_inline_klass()->default_instance()));
constant = new Constant(new InstanceConstant(field->type()->as_inline_klass()->default_instance()));
} else 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()) {
if (field->signature()->is_Q_signature() && field_value.is_null_or_zero()) {
if (field->is_null_free() && field_value.is_null_or_zero()) {
// Non-flattened inline type field. Replace null by the default value.
constant = new Constant(new InstanceConstant(field->type()->unwrap()->as_inline_klass()->default_instance()));
constant = new Constant(new InstanceConstant(field->type()->as_inline_klass()->default_instance()));
} else {
constant = make_constant(field_value, field);
}
@@ -1985,7 +1985,7 @@ void GraphBuilder::access_field(Bytecodes::Code code) {
set_pending_field_access(dfa);
}
} else {
ciInlineKlass* inline_klass = field->type()->unwrap()->as_inline_klass();
ciInlineKlass* inline_klass = field->type()->as_inline_klass();
scope()->set_wrote_final();
scope()->set_wrote_fields();
bool need_membar = false;
@@ -2042,7 +2042,7 @@ void GraphBuilder::access_field(Bytecodes::Code code) {
Value mask = append(new Constant(new IntConstant(1)));
val = append(new LogicOp(Bytecodes::_iand, val, mask));
}
if (field_type == T_INLINE_TYPE && field->type()->is_null_free() && field->type()->unwrap()->as_inline_klass()->is_empty()) {
if (field->is_null_free() && field->type()->is_loaded() && field->type()->as_inline_klass()->is_empty()) {
// Storing to a field of an empty inline type. Ignore.
null_check(obj);
} else if (!field->is_flattened()) {
@@ -2053,7 +2053,7 @@ void GraphBuilder::access_field(Bytecodes::Code code) {
}
} else {
assert(!needs_patching, "Can't patch flattened inline type field access");
ciInlineKlass* inline_klass = field->type()->unwrap()->as_inline_klass();
ciInlineKlass* inline_klass = field->type()->as_inline_klass();
copy_inline_content(inline_klass, val, inline_klass->first_field_offset(), obj, offset, state_before, field);
}
break;
@@ -2129,7 +2129,7 @@ void GraphBuilder::withfield(int field_index) {
}
if (field_modify->is_flattened()) {
assert(!needs_patching, "Can't patch flattened inline type field access");
ciInlineKlass* vk = field_modify->type()->unwrap()->as_inline_klass();
ciInlineKlass* vk = field_modify->type()->as_inline_klass();
if (!vk->is_empty()) {
copy_inline_content(vk, val, vk->first_field_offset(), new_instance, offset_modify, state_before, field_modify);
}
@@ -2478,7 +2478,7 @@ void GraphBuilder::invoke(Bytecodes::Code code) {
}

Invoke* result = new Invoke(code, result_type, recv, args, target, state_before,
declared_signature->return_type()->is_null_free());
declared_signature->returns_null_free_inline_type());
// push result
append_split(result);

@@ -3579,7 +3579,7 @@ ValueStack* GraphBuilder::state_at_entry() {
// don't allow T_ARRAY to propagate into locals types
if (is_reference_type(basic_type)) basic_type = T_OBJECT;
ValueType* vt = as_ValueType(basic_type);
state->store_local(idx, new Local(type, vt, idx, false, type->is_null_free()));
state->store_local(idx, new Local(type, vt, idx, false, sig->is_null_free_at(i)));
idx += type->size();
}

@@ -108,7 +108,7 @@ void Instruction::state_values_do(ValueVisitor* f) {
}

ciType* Instruction::exact_type() const {
ciType* t = declared_type();
ciType* t = declared_type();
if (t != NULL && t->is_klass()) {
return t->as_klass()->exact_klass();
}
@@ -138,15 +138,13 @@ bool Instruction::maybe_flattened_array() {
if (UseFlatArray) {
ciType* type = declared_type();
if (type != NULL) {
if (type->is_obj_array_klass()) {
// Due to array covariance, the runtime type might be a flattened array.
if (type->is_obj_array_klass() && !type->as_obj_array_klass()->is_elem_null_free()) {
// The runtime type of [LMyValue might be [QMyValue due to [QMyValue <: [LMyValue.
Copy link
Collaborator

@fparain fparain May 28, 2021

Choose a reason for hiding this comment

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

if type->is_obj_array_klass() is true and type->as_obj_array_klass()->is_elem_null_free() is also true, the method returns false, is it the expected behavior?

Copy link
Member Author

@TobiHartmann TobiHartmann May 31, 2021

Choose a reason for hiding this comment

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

Yes, because in that case we have a null-free but non-flattened array. Right?

Copy link
Collaborator

@fparain fparain May 31, 2021

Choose a reason for hiding this comment

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

Correct. Got it.

ciKlass* element_klass = type->as_obj_array_klass()->element_klass();
if (element_klass->can_be_inline_klass() && (!element_klass->is_inlinetype() || element_klass->as_inline_klass()->flatten_array())) {
return true;
}
} else if (type->is_flat_array_klass()) {
ciKlass* element_klass = type->as_flat_array_klass()->element_klass();
assert(!element_klass->is_loaded() || element_klass->flatten_array(), "must be flattened");
return true;
} else if (type->is_klass() && type->as_klass()->is_java_lang_Object()) {
// This can happen as a parameter to System.arraycopy()
@@ -166,8 +164,7 @@ bool Instruction::maybe_null_free_array() {
if (type != NULL) {
if (type->is_obj_array_klass()) {
// Due to array covariance, the runtime type might be a null-free array.
ciKlass* element_klass = type->as_obj_array_klass()->element_klass();
if (element_klass->can_be_inline_klass()) {
if (type->as_obj_array_klass()->can_be_inline_array_klass()) {
return true;
}
}
@@ -268,7 +265,7 @@ ciType* LoadIndexed::declared_type() const {
bool StoreIndexed::is_exact_flattened_array_store() const {
if (array()->is_loaded_flattened_array() && value()->as_Constant() == NULL && value()->declared_type() != NULL) {
ciKlass* element_klass = array()->declared_type()->as_flat_array_klass()->element_klass();
ciKlass* actual_klass = value()->declared_type()->unwrap()->as_klass();
ciKlass* actual_klass = value()->declared_type()->as_klass();

// The following check can fail with inlining:
// void test45_inline(Object[] oa, Object o, int index) { oa[index] = o; }
@@ -866,7 +866,7 @@ LEAF(LoadField, AccessField)
ciInlineKlass* inline_klass = NULL, Value default_value = NULL )
: AccessField(obj, offset, field, is_static, state_before, needs_patching)
{
set_null_free(field->signature()->is_Q_signature());
set_null_free(field->is_null_free());
}

ciType* declared_type() const;
@@ -1747,13 +1747,13 @@ void LIRGenerator::access_sub_element(LIRItem& array, LIRItem& index, LIR_Opr& r
elm_item, LIR_OprFact::intConst(sub_offset), result,
NULL, NULL);

if (field->signature()->is_Q_signature()) {
assert(field->type()->unwrap()->as_inline_klass()->is_loaded(), "Must be");
if (field->is_null_free()) {
assert(field->type()->as_inline_klass()->is_loaded(), "Must be");
LabelObj* L_end = new LabelObj();
__ cmp(lir_cond_notEqual, result, LIR_OprFact::oopConst(NULL));
__ branch(lir_cond_notEqual, L_end->label());
set_in_conditional_code(true);
Constant* default_value = new Constant(new InstanceConstant(field->type()->unwrap()->as_inline_klass()->default_instance()));
Constant* default_value = new Constant(new InstanceConstant(field->type()->as_inline_klass()->default_instance()));
if (default_value->is_pinned()) {
__ move(LIR_OprFact::value_type(default_value->type()), result);
} else {
@@ -1773,7 +1773,7 @@ void LIRGenerator::access_flattened_array(bool is_load, LIRItem& array, LIRItem&

ciInlineKlass* elem_klass = NULL;
if (field != NULL) {
elem_klass = field->type()->unwrap()->as_inline_klass();
elem_klass = field->type()->as_inline_klass();
} else {
elem_klass = array.value()->declared_type()->as_flat_array_klass()->element_klass()->as_inline_klass();
}
@@ -2046,7 +2046,7 @@ LIR_Opr LIRGenerator::access_atomic_add_at(DecoratorSet decorators, BasicType ty
bool LIRGenerator::inline_type_field_access_prolog(AccessField* x, CodeEmitInfo* info) {
ciField* field = x->field();
assert(!field->is_flattened(), "Flattened field access should have been expanded");
if (!field->signature()->is_Q_signature()) {
if (!field->is_null_free()) {
return true; // Not an inline type field
}
// Deoptimize if the access is non-static and requires patching (holder not loaded
@@ -2055,7 +2055,7 @@ bool LIRGenerator::inline_type_field_access_prolog(AccessField* x, CodeEmitInfo*
bool could_be_flat = !x->is_static() && x->needs_patching();
// Deoptimize if we load from a static field with an unloaded type because we need
// the default value if the field is null.
bool could_be_null = x->is_static() && x->as_LoadField() != NULL && !field->type()->unwrap()->is_loaded();
bool could_be_null = x->is_static() && x->as_LoadField() != NULL && !field->type()->is_loaded();
assert(!could_be_null || !field->holder()->is_loaded(), "inline type field should be loaded");
if (could_be_flat || could_be_null) {
assert(x->needs_patching(), "no deopt required");
@@ -2134,10 +2134,10 @@ void LIRGenerator::do_LoadField(LoadField* x) {
info ? new CodeEmitInfo(info) : NULL, info);

ciField* field = x->field();
if (field->signature()->is_Q_signature()) {
if (field->is_null_free()) {
// Load from non-flattened inline type field requires
// a null check to replace null with the default value.
ciInlineKlass* inline_klass = field->type()->unwrap()->as_inline_klass();
ciInlineKlass* inline_klass = field->type()->as_inline_klass();
assert(inline_klass->is_loaded(), "field klass must be loaded");

ciInstanceKlass* holder = field->holder();
@@ -3053,11 +3053,6 @@ ciKlass* LIRGenerator::profile_type(ciMethodData* md, int md_base_offset, int md
do_update = exact_klass == NULL || ciTypeEntries::valid_ciklass(profiled_k) != exact_klass;
}

// Inline types can't be null
if (exact_klass != NULL && exact_klass->is_inlinetype()) {
do_null = false;
}

if (!do_null && !do_update) {
return result;
}
@@ -102,27 +102,22 @@ bool ciArrayKlass::is_leaf_type() {
// ciArrayKlass::base_element_type
//
// What type is obtained when this array is indexed as many times as possible?
ciArrayKlass* ciArrayKlass::make(ciType* element_type) {
ciArrayKlass* ciArrayKlass::make(ciType* element_type, bool null_free) {
if (element_type->is_primitive_type()) {
return ciTypeArrayKlass::make(element_type->basic_type());
} else {
return make(element_type->as_klass(), element_type->is_null_free());
}
}

ciArrayKlass* ciArrayKlass::make(ciKlass* klass, bool null_free) {
if (null_free && klass->is_loaded()) {
GUARDED_VM_ENTRY(
EXCEPTION_CONTEXT;
Klass* ak = InlineKlass::cast(klass->get_Klass())->null_free_inline_array_klass(THREAD);
if (HAS_PENDING_EXCEPTION) {
CLEAR_PENDING_EXCEPTION;
} else if (ak != NULL && ak->is_flatArray_klass()) {
return ciFlatArrayKlass::make(klass);
}
)
return ciObjArrayKlass::make(klass, true);
} else {
ciKlass* klass = element_type->as_klass();
if (null_free && klass->is_loaded()) {
GUARDED_VM_ENTRY(
EXCEPTION_CONTEXT;
Klass* ak = InlineKlass::cast(klass->get_Klass())->null_free_inline_array_klass(THREAD);
if (HAS_PENDING_EXCEPTION) {
CLEAR_PENDING_EXCEPTION;
} else if (ak != NULL && ak->is_flatArray_klass()) {
return ciFlatArrayKlass::make(klass);
}
)
}
return ciObjArrayKlass::make(klass, null_free);
}
}
@@ -59,11 +59,12 @@ class ciArrayKlass : public ciKlass {
// The one-level type of the array elements.
virtual ciKlass* element_klass() { return NULL; }

static ciArrayKlass* make(ciType* element_type);
static ciArrayKlass* make(ciKlass* klass, bool null_free);
static ciArrayKlass* make(ciType* klass, bool null_free = false);

int array_header_in_bytes();
ciInstance* component_mirror_instance() const;

virtual bool is_elem_null_free() const { return false; }
};

#endif // SHARE_CI_CIARRAYKLASS_HPP
@@ -98,12 +98,7 @@ ciField::ciField(ciInstanceKlass* klass, int index) :
bool ignore;
// This is not really a class reference; the index always refers to the
// field's type signature, as a symbol. Linkage checks do not apply.
ciType* type = ciEnv::current(THREAD)->get_klass_by_index(cpool, sig_index, ignore, klass);
if (signature->is_Q_signature()) {
_type = ciEnv::current(THREAD)->make_null_free_wrapper(type);
} else {
_type = type;
}
_type = ciEnv::current(THREAD)->get_klass_by_index(cpool, sig_index, ignore, klass);
} else {
_type = ciType::make(field_type);
}
@@ -381,12 +376,6 @@ ciType* ciField::compute_type() {

ciType* ciField::compute_type_impl() {
ciKlass* type = CURRENT_ENV->get_klass_by_name_impl(_holder, constantPoolHandle(), _signature, false);
ciType* rtype;
if (_signature->is_Q_signature()) {
rtype = CURRENT_ENV->make_null_free_wrapper(type);
} else {
rtype = type;
}
if (!type->is_primitive_type() && is_shared()) {
// We must not cache a pointer to an unshared type, in a shared field.
bool type_is_also_shared = false;
@@ -398,12 +387,11 @@ ciType* ciField::compute_type_impl() {
// Currently there is no 'shared' query for array types.
type_is_also_shared = !ciObjectFactory::is_initialized();
}
if (!type_is_also_shared) {
return rtype; // Bummer.
}
if (!type_is_also_shared)
return type; // Bummer.
}
_type = rtype;
return rtype;
_type = type;
return type;
}


@@ -84,6 +84,8 @@ class ciFlatArrayKlass : public ciArrayKlass {
virtual bool can_be_inline_array_klass() {
return true;
}

virtual bool is_elem_null_free() const { return true; }
};