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

8273594: [lworld] JITs need to properly handle static inline type field with unloaded type #551

Closed
wants to merge 9 commits into from
@@ -1840,7 +1840,8 @@ 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->is_null_free() && field->type()->is_loaded() && field->type()->as_inline_klass()->is_empty()) {
} 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.
constant = new Constant(new InstanceConstant(field->type()->as_inline_klass()->default_instance()));
}
@@ -1649,7 +1649,7 @@ void LIRGenerator::do_StoreField(StoreField* x) {
}
#endif

if (!inline_type_field_access_prolog(x, info)) {
if (!inline_type_field_access_prolog(x)) {
// Field store will always deopt due to unloaded field or holder klass
return;
}
@@ -2034,7 +2034,7 @@ LIR_Opr LIRGenerator::access_atomic_add_at(DecoratorSet decorators, BasicType ty
}
}

bool LIRGenerator::inline_type_field_access_prolog(AccessField* x, CodeEmitInfo* info) {
bool LIRGenerator::inline_type_field_access_prolog(AccessField* x) {
ciField* field = x->field();
assert(!field->is_flattened(), "Flattened field access should have been expanded");
if (!field->is_null_free()) {
@@ -2044,12 +2044,12 @@ bool LIRGenerator::inline_type_field_access_prolog(AccessField* x, CodeEmitInfo*
// or not accessible) because then we only have partial field information and the
// field could be flattened (see ciField constructor).
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()->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");
// Deoptimize if we load from a static field with an uninitialized type because we
// need to throw an exception if initialization of the type failed.
bool not_initialized = x->is_static() && x->as_LoadField() != NULL &&
!field->type()->as_instance_klass()->is_initialized();
if (could_be_flat || not_initialized) {
CodeEmitInfo* info = state_for(x, x->state_before());
CodeStub* stub = new DeoptimizeStub(new CodeEmitInfo(info),
Deoptimization::Reason_unloaded,
Deoptimization::Action_make_not_entrant);
@@ -2088,7 +2088,7 @@ void LIRGenerator::do_LoadField(LoadField* x) {
}
#endif

if (!inline_type_field_access_prolog(x, info)) {
if (!inline_type_field_access_prolog(x)) {
// Field load will always deopt due to unloaded field or holder klass
LIR_Opr result = rlock_result(x, field_type);
__ move(LIR_OprFact::oopConst(NULL), result);
@@ -2128,9 +2128,6 @@ void LIRGenerator::do_LoadField(LoadField* x) {
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()->as_inline_klass();
assert(inline_klass->is_loaded(), "field klass must be loaded");

ciInstanceKlass* holder = field->holder();
if (field->is_static() && holder->is_loaded()) {
ciObject* val = holder->java_mirror()->field_value(field).as_object();
@@ -2143,6 +2140,7 @@ void LIRGenerator::do_LoadField(LoadField* x) {
__ cmp(lir_cond_notEqual, result, LIR_OprFact::oopConst(NULL));
__ branch(lir_cond_notEqual, L_end->label());
set_in_conditional_code(true);
ciInlineKlass* inline_klass = field->type()->as_inline_klass();
Constant* default_value = new Constant(new InstanceConstant(inline_klass->default_instance()));
if (default_value->is_pinned()) {
__ move(LIR_OprFact::value_type(default_value->type()), result);
@@ -271,7 +271,7 @@ class LIRGenerator: public InstructionVisitor, public BlockClosure {
void do_vectorizedMismatch(Intrinsic* x);
void do_blackhole(Intrinsic* x);

bool inline_type_field_access_prolog(AccessField* x, CodeEmitInfo* info);
bool inline_type_field_access_prolog(AccessField* x);
void access_flattened_array(bool is_load, LIRItem& array, LIRItem& index, LIRItem& obj_item, ciField* field = NULL, int offset = 0);
void access_sub_element(LIRItem& array, LIRItem& index, LIR_Opr& result, ciField* field, int sub_offset);
LIR_Opr get_and_load_element_address(LIRItem& array, LIRItem& index);
@@ -669,7 +669,16 @@ void ciTypeFlow::StateVector::do_getstatic(ciBytecodeStream* str) {
trap(str, field->holder(), str->get_field_holder_index());
} else {
ciType* field_type = field->type();
if (!field_type->is_loaded()) {
if (field->is_static() && field->is_null_free() &&
!field_type->as_instance_klass()->is_initialized()) {
// Deoptimize if we load from a static field with an uninitialized inline type
// because we need to throw an exception if initialization of the type failed.
trap(str, field_type->as_klass(),
Deoptimization::make_trap_request
(Deoptimization::Reason_unloaded,
Deoptimization::Action_reinterpret));
return;
} else if (!field_type->is_loaded()) {
// Normally, we need the field's type to be loaded if we are to
// do anything interesting with its value.
// We used to do this: trap(str, str->get_field_signature_index());
@@ -240,8 +240,11 @@ private void start() {
}

private void setupTests() {
for (Class<?> clazz : testClass.getDeclaredClasses()) {
checkAnnotationsInClass(clazz, "inner");
// TODO remove this once JDK-8273591 is fixed
if (!IGNORE_COMPILER_CONTROLS) {
for (Class<?> clazz : testClass.getDeclaredClasses()) {
checkAnnotationsInClass(clazz, "inner");
}
}
if (DUMP_REPLAY) {
addReplay();
@@ -23,10 +23,7 @@

package compiler.valhalla.inlinetypes;

import compiler.lib.ir_framework.Run;
import compiler.lib.ir_framework.RunInfo;
import compiler.lib.ir_framework.Scenario;
import compiler.lib.ir_framework.Test;
import compiler.lib.ir_framework.*;
import jdk.test.lib.Asserts;

import static compiler.valhalla.inlinetypes.InlineTypes.rI;
@@ -52,17 +49,17 @@ public static void main(String[] args) {
final Scenario[] scenarios = {
new Scenario(0),
new Scenario(1, "-XX:InlineFieldMaxFlatSize=0"),
new Scenario(2, "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-XX:+PatchALot"),
new Scenario(3,
"-XX:InlineFieldMaxFlatSize=0",
"-XX:+IgnoreUnrecognizedVMOptions",
"-XX:+PatchALot")
new Scenario(2, "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+PatchALot"),
new Scenario(3, "-XX:InlineFieldMaxFlatSize=0",
"-XX:+IgnoreUnrecognizedVMOptions", "-XX:+PatchALot")
};
final String[] CutoffFlags = {"-XX:PerMethodRecompilationCutoff=-1", "-XX:PerBytecodeRecompilationCutoff=-1"};
final String[] flags = {// Prevent IR Test Framework from loading classes
"-DIgnoreCompilerControls=true",
// Some tests trigger frequent re-compilation. Don't mark them as non-compilable.
"-XX:PerMethodRecompilationCutoff=-1", "-XX:PerBytecodeRecompilationCutoff=-1"};
for (Scenario s : scenarios) {
s.addFlags(CutoffFlags);
s.addFlags(flags);
}

InlineTypes.getFramework()
.addScenarios(scenarios)
.start();
@@ -883,4 +880,221 @@ public void test20_verifier() {
MyValue20 vt = test20();
Asserts.assertEQ(vt.obj, null);
}

static primitive class Test21ClassA {
static Test21ClassB b;
static Test21ClassC c;
}

static primitive class Test21ClassB {
static int x = Test21ClassA.c.x;
}

static primitive class Test21ClassC {
int x = 42;
}

// Test access to static inline type field with unloaded type
@Test
public Object test21() {
return new Test21ClassA();
}

@Run(test = "test21")
public void test21_verifier() {
Object ret = test21();
Asserts.assertEQ(Test21ClassA.b.x, 0);
Asserts.assertEQ(Test21ClassA.c.x, 0);
}

static boolean test22FailInit = true;

static primitive class Test22ClassA {
int x = 0;
static Test22ClassB b;
}

static primitive class Test22ClassB {
int x = 0;
static {
if (test22FailInit) {
throw new RuntimeException();
}
}
}

// Test that load from static field of uninitialized inline type throws an exception
@Test
public Object test22() {
return Test22ClassA.b;
}

@Run(test = "test22")
public void test22_verifier() {
// Trigger initialization error in Test22ClassB
try {
Test22ClassB b = new Test22ClassB();
throw new RuntimeException("Should have thrown error during initialization");
} catch (ExceptionInInitializerError | NoClassDefFoundError e) {
// Expected
}
try {
test22();
throw new RuntimeException("Should have thrown NoClassDefFoundError");
} catch (NoClassDefFoundError e) {
// Expected
}
}

static boolean test23FailInit = true;

static primitive class Test23ClassA {
int x = 0;
static Test23ClassB b;
}

static primitive class Test23ClassB {
static {
if (test23FailInit) {
throw new RuntimeException();
}
}
}

// Same as test22 but with empty ClassB
@Test
public Object test23() {
return Test23ClassA.b;
}

@Run(test = "test23")
public void test23_verifier() {
// Trigger initialization error in Test23ClassB
try {
Test23ClassB b = new Test23ClassB();
throw new RuntimeException("Should have thrown error during initialization");
} catch (ExceptionInInitializerError | NoClassDefFoundError e) {
// Expected
}
try {
test23();
throw new RuntimeException("Should have thrown NoClassDefFoundError");
} catch (NoClassDefFoundError e) {
// Expected
}
}

static boolean test24FailInit = true;

static primitive class Test24ClassA {
Test24ClassB b = Test24ClassB.default;
}

static primitive class Test24ClassB {
int x = 0;
static {
if (test24FailInit) {
throw new RuntimeException();
}
}
}

// Test that access to non-static field of uninitialized inline type throws an exception
@Test
public Object test24() {
return Test24ClassA.default.b.x;
}

@Run(test = "test24")
public void test24_verifier() {
// Trigger initialization error in Test24ClassB
try {
Test24ClassB b = new Test24ClassB();
throw new RuntimeException("Should have thrown error during initialization");
} catch (ExceptionInInitializerError | NoClassDefFoundError e) {
// Expected
}
try {
test24();
throw new RuntimeException("Should have thrown NoClassDefFoundError");
} catch (NoClassDefFoundError e) {
// Expected
}
}

static boolean test25FailInit = true;

static primitive class Test25ClassA {
Test25ClassB b = Test25ClassB.default;
}

static primitive class Test25ClassB {
int x = 24;
static {
if (test25FailInit) {
throw new RuntimeException();
}
}
}

// Same as test24 but with field access outside of test method
@Test
public Test25ClassB test25() {
return Test25ClassA.default.b;
}

@Run(test = "test25")
public void test25_verifier() {
// Trigger initialization error in Test25ClassB
try {
Test25ClassB b = new Test25ClassB();
throw new RuntimeException("Should have thrown error during initialization");
} catch (ExceptionInInitializerError | NoClassDefFoundError e) {
// Expected
}
try {
Test25ClassB res = test25();
Asserts.assertEQ(res.x, 0);
throw new RuntimeException("Should have thrown NoClassDefFoundError");
} catch (NoClassDefFoundError e) {
// Expected
}
}

static boolean test26FailInit = true;

static primitive class Test26ClassA {
Test26ClassB b = Test26ClassB.default;
}

static primitive class Test26ClassB {
static {
if (test26FailInit) {
throw new RuntimeException();
}
}
}

// Same as test25 but with empty ClassB
@Test
public Object test26() {
return Test26ClassA.default.b;
}

@Run(test = "test26")
public void test26_verifier() {
// Trigger initialization error in Test26ClassB
try {
Test26ClassB b = new Test26ClassB();
throw new RuntimeException("Should have thrown error during initialization");
} catch (ExceptionInInitializerError | NoClassDefFoundError e) {
// Expected
}
try {
test26();
throw new RuntimeException("Should have thrown NoClassDefFoundError");
} catch (NoClassDefFoundError e) {
// Expected
}
}
}
@@ -29,7 +29,7 @@
* @summary Test initialization of static inline fields with circularity
* @library /test/lib
* @compile CircularityTest.java
* @run main/othervm -Xint runtime.valhalla.inlinetypes.CircularityTest
* @run main runtime.valhalla.inlinetypes.CircularityTest
*/