Skip to content

Commit

Permalink
C1 support
Browse files Browse the repository at this point in the history
  • Loading branch information
TobiHartmann committed Dec 6, 2024
1 parent 6338af2 commit c12f3c1
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 23 deletions.
29 changes: 22 additions & 7 deletions src/hotspot/share/c1/c1_GraphBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1825,6 +1825,9 @@ void GraphBuilder::copy_inline_content(ciInlineKlass* vk, Value src, int src_off
assert(!inner_field->is_flat(), "the iteration over nested fields is handled by the loop itself");
int off = inner_field->offset_in_bytes() - vk->first_field_offset();
LoadField* load = new LoadField(src, src_off + off, inner_field, false, state_before, false);
// TODO no null checks on holder required after first field copy
// load->needs_null_check(false)
// dest->needs_null_check(false)
Value replacement = append(load);
StoreField* store = new StoreField(dest, dest_off + off, inner_field, replacement, false, state_before, false);
store->set_enclosing_field(enclosing_field);
Expand Down Expand Up @@ -1886,7 +1889,7 @@ void GraphBuilder::access_field(Bytecodes::Code code) {
constant = make_constant(field_value, field);
} else if (field->is_null_free() && field->type()->as_instance_klass()->is_initialized() &&
field->type()->as_inline_klass()->is_empty()) {
// Loading from a field of an empty inline type. Just return the default instance.
// Loading from a field of an empty, null-free inline type. Just return the default instance.
constant = new Constant(new InstanceConstant(field->type()->as_inline_klass()->default_instance()));
}
if (constant != nullptr) {
Expand Down Expand Up @@ -1914,7 +1917,7 @@ void GraphBuilder::access_field(Bytecodes::Code code) {
null_check(val);
}
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.
// Storing to a field of an empty, null-free inline type. Ignore.
break;
}
append(new StoreField(append(obj), offset, field, val, true, state_before, needs_patching));
Expand All @@ -1933,7 +1936,7 @@ void GraphBuilder::access_field(Bytecodes::Code code) {
ObjectType* obj_type = obj->type()->as_ObjectType();
if (field->is_null_free() && field->type()->as_instance_klass()->is_initialized()
&& field->type()->as_inline_klass()->is_empty()) {
// Loading from a field of an empty inline type. Just return the default instance.
// Loading from a field of an empty, null-free inline type. Just return the default instance.
null_check(obj);
constant = new Constant(new InstanceConstant(field->type()->as_inline_klass()->default_instance()));
} else if (field->is_constant() && !field->is_flat() && obj_type->is_constant() && !PatchALot) {
Expand Down Expand Up @@ -2006,7 +2009,14 @@ void GraphBuilder::access_field(Bytecodes::Code code) {
} else {
push(type, append(load));
}
} else { // field is flat
} else if (!field->is_null_free()) {
// Flat and nullable
assert(!needs_patching, "Can't patch flat inline type field access");
LoadField* load = new LoadField(obj, offset, field, false, state_before, needs_patching);
push(type, append(load));
} else {
// Flat and null-free
assert(!needs_patching, "Can't patch flat inline type field access");
// Look at the next bytecode to check if we can delay the field access
bool can_delay_access = false;
ciBytecodeStream s(method());
Expand Down Expand Up @@ -2034,7 +2044,7 @@ void GraphBuilder::access_field(Bytecodes::Code code) {
scope()->set_wrote_final();
scope()->set_wrote_fields();
bool need_membar = false;
if (inline_klass->is_initialized() && inline_klass->is_empty()) {
if (field->is_null_free() && inline_klass->is_initialized() && inline_klass->is_empty()) {
apush(append(new Constant(new InstanceConstant(inline_klass->default_instance()))));
if (has_pending_field_access()) {
set_pending_field_access(nullptr);
Expand All @@ -2058,7 +2068,6 @@ void GraphBuilder::access_field(Bytecodes::Code code) {
NewInstance* new_instance = new NewInstance(inline_klass, state_before, false, true);
_memory->new_instance(new_instance);
apush(append_split(new_instance));
assert(!needs_patching, "Can't patch flat inline type field access");
if (has_pending_field_access()) {
copy_inline_content(inline_klass, pending_field_access()->obj(),
pending_field_access()->offset() + field->offset_in_bytes() - field->holder()->as_inline_klass()->first_field_offset(),
Expand Down Expand Up @@ -2091,7 +2100,7 @@ void GraphBuilder::access_field(Bytecodes::Code code) {
val = append(new LogicOp(Bytecodes::_iand, val, mask));
}
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.
// Storing to a field of an empty, null-free inline type. Ignore.
null_check(obj);
null_check(val);
} else if (!field->is_flat()) {
Expand All @@ -2103,7 +2112,13 @@ void GraphBuilder::access_field(Bytecodes::Code code) {
if (store != nullptr) {
append(store);
}
} else if (!field->is_null_free()) {
// Flat and nullable
assert(!needs_patching, "Can't patch flat inline type field access");
StoreField* store = new StoreField(obj, offset, field, val, false, state_before, needs_patching);
append(store);
} else {
// Flat and null-free
assert(!needs_patching, "Can't patch flat inline type field access");
ciInlineKlass* inline_klass = field->type()->as_inline_klass();
copy_inline_content(inline_klass, val, inline_klass->first_field_offset(), obj, offset, state_before, field);
Expand Down
88 changes: 84 additions & 4 deletions src/hotspot/share/c1/c1_LIRGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1635,6 +1635,30 @@ void LIRGenerator::do_CompareAndSwap(Intrinsic* x, ValueType* type) {
set_result(x, result);
}

// Convert size in bytes to corresponding BasicType
static BasicType size_to_basic_type(int size) {
BasicType bt;
if (size == sizeof(jlong)) {
bt = T_LONG;
} else if (size == sizeof(jint)) {
bt = T_INT;
} else if (size == sizeof(jshort)) {
bt = T_SHORT;
} else if (size == sizeof(jbyte)) {
bt = T_BYTE;
} else {
assert(false, "unsupported size: %d", size);
}
return bt;
}

// Returns a int/long value with the null marker bit set
static LIR_Opr null_marker_mask(BasicType bt, ciField* field) {
int nm_offset = field->null_marker_offset() - field->offset_in_bytes();
unsigned long null_marker = 1UL << (nm_offset << LogBitsPerByte);
return (bt == T_LONG) ? LIR_OprFact::longConst(null_marker) : LIR_OprFact::intConst(null_marker);
}

// Comment copied form templateTable_i486.cpp
// ----------------------------------------------------------------------------
// Volatile variables demand their effects be made known to all CPU's in
Expand Down Expand Up @@ -1664,8 +1688,9 @@ void LIRGenerator::do_CompareAndSwap(Intrinsic* x, ValueType* type) {


void LIRGenerator::do_StoreField(StoreField* x) {
ciField* field = x->field();
bool needs_patching = x->needs_patching();
bool is_volatile = x->field()->is_volatile();
bool is_volatile = field->is_volatile();
BasicType field_type = x->field_type();

CodeEmitInfo* info = nullptr;
Expand Down Expand Up @@ -1696,7 +1721,7 @@ void LIRGenerator::do_StoreField(StoreField* x) {
} else {
value.load_item();
}
} else {
} else if (!field->is_flat()) {
value.load_for_store(field_type);
}

Expand Down Expand Up @@ -1726,6 +1751,38 @@ void LIRGenerator::do_StoreField(StoreField* x) {
decorators |= C1_NEEDS_PATCHING;
}

if (field->is_flat()) {
assert(!field->is_null_free(), "Null free flat fields are handled in high level IR");
ciInlineKlass* vk = field->type()->as_inline_klass();
BasicType bt = size_to_basic_type(vk->nullable_size_in_bytes());

// Zero the payload
LIR_Opr payload = new_register((bt == T_LONG) ? bt : T_INT);
LIR_Opr zero = (bt == T_LONG) ? LIR_OprFact::longConst(0) : LIR_OprFact::intConst(0);
__ move(zero, payload);

bool is_constant_null = value.is_constant() && value.value()->is_null_obj();
if (!is_constant_null) {
LabelObj* L_isNull = new LabelObj();
bool needs_null_check = !value.is_constant() || value.value()->is_null_obj();
if (needs_null_check) {
__ cmp(lir_cond_equal, value.result(), LIR_OprFact::oopConst(nullptr));
__ branch(lir_cond_equal, L_isNull->label());
}
// Load payload (if not empty) and set null marker
if (bt != T_BYTE) {
access_load_at(decorators, bt, value, LIR_OprFact::intConst(vk->first_field_offset()), payload);
}
__ logical_or(payload, null_marker_mask(bt, field), payload);

if (needs_null_check) {
__ branch_destination(L_isNull->label());
}
}
access_store_at(decorators, bt, object, LIR_OprFact::intConst(field->offset_in_bytes()), payload);
return;
}

access_store_at(decorators, field_type, object, LIR_OprFact::intConst(x->offset()),
value.result(), info != nullptr ? new CodeEmitInfo(info) : nullptr, info);
}
Expand Down Expand Up @@ -2096,8 +2153,9 @@ LIR_Opr LIRGenerator::access_atomic_add_at(DecoratorSet decorators, BasicType ty
}

void LIRGenerator::do_LoadField(LoadField* x) {
ciField* field = x->field();
bool needs_patching = x->needs_patching();
bool is_volatile = x->field()->is_volatile();
bool is_volatile = field->is_volatile();
BasicType field_type = x->field_type();

CodeEmitInfo* info = nullptr;
Expand Down Expand Up @@ -2148,12 +2206,34 @@ void LIRGenerator::do_LoadField(LoadField* x) {
decorators |= C1_NEEDS_PATCHING;
}

if (field->is_flat()) {
assert(!field->is_null_free(), "Null free flat fields are handled in high level IR");
assert(x->state_before() != nullptr, "Needs state before");
ciInlineKlass* vk = field->type()->as_inline_klass();
BasicType bt = size_to_basic_type(vk->nullable_size_in_bytes());

// Allocate buffer
NewInstance* buffer = new NewInstance(vk, x->state_before(), false, true);
do_NewInstance(buffer);
LIRItem dest(buffer, this);

// Copy the payload to the buffer
LIR_Opr payload = new_register((bt == T_LONG) ? bt : T_INT);
access_load_at(decorators, bt, object, LIR_OprFact::intConst(field->offset_in_bytes()), payload);
access_store_at(decorators, bt, dest, LIR_OprFact::intConst(vk->first_field_offset()), payload);

// Check the null marker and set result to null if not set
__ logical_and(payload, null_marker_mask(bt, field), payload);
__ cmp(lir_cond_notEqual, payload, (bt == T_LONG) ? LIR_OprFact::longConst(0) : LIR_OprFact::intConst(0));
__ cmove(lir_cond_notEqual, buffer->operand(), LIR_OprFact::oopConst(nullptr), rlock_result(x), T_OBJECT);
return;
}

LIR_Opr result = rlock_result(x, field_type);
access_load_at(decorators, field_type,
object, LIR_OprFact::intConst(x->offset()), result,
info ? new CodeEmitInfo(info) : nullptr, info);

ciField* field = x->field();
if (field->is_null_free()) {
// Load from non-flat inline type field requires
// a null check to replace null with the default value.
Expand Down
5 changes: 5 additions & 0 deletions src/hotspot/share/ci/ciInlineKlass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,8 @@ InlineKlass* ciInlineKlass::get_InlineKlass() const {
GUARDED_VM_ENTRY(return to_InlineKlass();)
}

int ciInlineKlass::nullable_size_in_bytes() const {
VM_ENTRY_MARK
assert(get_InlineKlass()->has_nullable_layout(), "Layout not available");
return get_InlineKlass()->nullable_size_in_bytes();
}
1 change: 1 addition & 0 deletions src/hotspot/share/ci/ciInlineKlass.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ class ciInlineKlass : public ciInstanceKlass {
address pack_handler() const;
address unpack_handler() const;
InlineKlass* get_InlineKlass() const;
int nullable_size_in_bytes() const;
};

#endif // SHARE_VM_CI_CIINLINEKLASS_HPP
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@

import jdk.test.lib.Asserts;

// TODO test with floats/doubles, also add oops

/*
* @test
* @key randomness
Expand All @@ -37,21 +39,21 @@
* @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64")
* @enablePreview
* @modules java.base/jdk.internal.vm.annotation
* @run main/othervm -XX:-TieredCompilation -Xbatch -XX:+NullableFieldFlattening
* @run main/othervm -Xbatch -XX:+NullableFieldFlattening
* compiler.valhalla.inlinetypes.TestFieldNullMarkers
* @run main/othervm -XX:-TieredCompilation -Xbatch -XX:+NullableFieldFlattening
* @run main/othervm -Xbatch -XX:+NullableFieldFlattening
* -XX:CompileCommand=dontinline,*::testHelper*
* compiler.valhalla.inlinetypes.TestFieldNullMarkers
* @run main/othervm -XX:-TieredCompilation -Xbatch -XX:+NullableFieldFlattening
* @run main/othervm -Xbatch -XX:+NullableFieldFlattening
* -XX:+InlineTypeReturnedAsFields -XX:+InlineTypePassFieldsAsArgs
* compiler.valhalla.inlinetypes.TestFieldNullMarkers
* @run main/othervm -XX:-TieredCompilation -Xbatch -XX:+NullableFieldFlattening
* @run main/othervm -Xbatch -XX:+NullableFieldFlattening
* -XX:-InlineTypeReturnedAsFields -XX:-InlineTypePassFieldsAsArgs
* compiler.valhalla.inlinetypes.TestFieldNullMarkers
* @run main/othervm -XX:-TieredCompilation -Xbatch -XX:+NullableFieldFlattening
* @run main/othervm -Xbatch -XX:+NullableFieldFlattening
* -XX:+InlineTypeReturnedAsFields -XX:-InlineTypePassFieldsAsArgs
* compiler.valhalla.inlinetypes.TestFieldNullMarkers
* @run main/othervm -XX:-TieredCompilation -Xbatch -XX:+NullableFieldFlattening
* @run main/othervm -Xbatch -XX:+NullableFieldFlattening
* -XX:-InlineTypeReturnedAsFields -XX:+InlineTypePassFieldsAsArgs
* compiler.valhalla.inlinetypes.TestFieldNullMarkers
*/
Expand Down Expand Up @@ -217,11 +219,17 @@ public MyValue7(MyValue6 val) {
}
}

MyValue1 field1;
MyValue4 field2;
MyValue5 field3;
MyValue6 field4;
MyValue7 field5;
MyValue1 field1; // Flat
MyValue4 field2; // Not flat
MyValue5 field3; // Not flat
MyValue6 field4; // Flat
MyValue7 field5; // Flat

static final MyValue1 VAL1 = new MyValue1((byte)42, new MyValue2((byte)43), null);
static final MyValue4 VAL4 = new MyValue4(new MyValue3((byte)42), null);
static final MyValue5 VAL5 = new MyValue5((byte)42, new MyValue5_1((byte)43, new MyValue5_2((byte)44, new MyValue5_3((byte)45))));
static final MyValue6 VAL6 = new MyValue6(new MyValueEmpty());
static final MyValue7 VAL7 = new MyValue7(new MyValue6(new MyValueEmpty()));

// Test that the calling convention is keeping track of the null marker
public MyValue1 testHelper1(MyValue1 val) {
Expand Down Expand Up @@ -521,7 +529,6 @@ public static void main(String[] args) {

t.testDeopt4(null, null, null, false);


t.field5 = null;
Asserts.assertEQ(t.testGet5(), null);

Expand All @@ -538,6 +545,28 @@ public static void main(String[] args) {
Asserts.assertEQ(t.testGet5().val, val6);

t.testDeopt5(null, null, null, null, false);

// Check accesses with constant value
t.field1 = VAL1;
Asserts.assertEQ(t.field1.x, VAL1.x);
Asserts.assertEQ(t.field1.val1, VAL1.val1);
Asserts.assertEQ(t.field1.val2, VAL1.val2);

t.field2 = VAL4;
Asserts.assertEQ(t.field2.val1, VAL4.val1);
Asserts.assertEQ(t.field2.val2, VAL4.val2);

t.field3 = VAL5;
Asserts.assertEQ(t.field3.x, VAL5.x);
Asserts.assertEQ(t.field3.val.x, VAL5.val.x);
Asserts.assertEQ(t.field3.val.val.x, VAL5.val.val.x);
Asserts.assertEQ(t.field3.val.val.val.x, VAL5.val.val.val.x);

t.field4 = VAL6;
Asserts.assertEQ(t.field4.val, VAL6.val);

t.field5 = VAL7;
Asserts.assertEQ(t.field5.val, VAL7.val);
}

// Trigger deoptimization to check that re-materialization takes the null marker into account
Expand All @@ -563,3 +592,4 @@ public static void main(String[] args) {
t.testDeopt5(val8, val9, val10, val11, false);
}
}

0 comments on commit c12f3c1

Please sign in to comment.