mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-26 14:24:46 +02:00
8231756: [JVMCI] need support for deoptimizing virtual byte arrays encoding non-byte primitives
Reviewed-by: kvn
This commit is contained in:
parent
430f8020fd
commit
12f8b52fb8
13 changed files with 332 additions and 72 deletions
|
@ -97,7 +97,7 @@ ScopeValue* DebugInfoReadStream::get_cached_object() {
|
|||
enum { LOCATION_CODE = 0, CONSTANT_INT_CODE = 1, CONSTANT_OOP_CODE = 2,
|
||||
CONSTANT_LONG_CODE = 3, CONSTANT_DOUBLE_CODE = 4,
|
||||
OBJECT_CODE = 5, OBJECT_ID_CODE = 6,
|
||||
AUTO_BOX_OBJECT_CODE = 7 };
|
||||
AUTO_BOX_OBJECT_CODE = 7, MARKER_CODE = 8 };
|
||||
|
||||
ScopeValue* ScopeValue::read_from(DebugInfoReadStream* stream) {
|
||||
ScopeValue* result = NULL;
|
||||
|
@ -110,6 +110,7 @@ ScopeValue* ScopeValue::read_from(DebugInfoReadStream* stream) {
|
|||
case OBJECT_CODE: result = stream->read_object_value(false /*is_auto_box*/); break;
|
||||
case AUTO_BOX_OBJECT_CODE: result = stream->read_object_value(true /*is_auto_box*/); break;
|
||||
case OBJECT_ID_CODE: result = stream->get_cached_object(); break;
|
||||
case MARKER_CODE: result = new MarkerValue(); break;
|
||||
default: ShouldNotReachHere();
|
||||
}
|
||||
return result;
|
||||
|
@ -130,6 +131,16 @@ void LocationValue::print_on(outputStream* st) const {
|
|||
location().print_on(st);
|
||||
}
|
||||
|
||||
// MarkerValue
|
||||
|
||||
void MarkerValue::write_on(DebugInfoWriteStream* stream) {
|
||||
stream->write_int(MARKER_CODE);
|
||||
}
|
||||
|
||||
void MarkerValue::print_on(outputStream* st) const {
|
||||
st->print("marker");
|
||||
}
|
||||
|
||||
// ObjectValue
|
||||
|
||||
void ObjectValue::set_value(oop value) {
|
||||
|
|
|
@ -50,6 +50,7 @@ class ScopeValue: public ResourceObj {
|
|||
virtual bool is_location() const { return false; }
|
||||
virtual bool is_object() const { return false; }
|
||||
virtual bool is_auto_box() const { return false; }
|
||||
virtual bool is_marker() const { return false; }
|
||||
virtual bool is_constant_int() const { return false; }
|
||||
virtual bool is_constant_double() const { return false; }
|
||||
virtual bool is_constant_long() const { return false; }
|
||||
|
@ -91,6 +92,19 @@ class LocationValue: public ScopeValue {
|
|||
void print_on(outputStream* st) const;
|
||||
};
|
||||
|
||||
// A placeholder value that has no concrete meaning other than helping constructing
|
||||
// other values.
|
||||
|
||||
class MarkerValue: public ScopeValue {
|
||||
public:
|
||||
bool is_marker() const { return true; }
|
||||
|
||||
// Serialization of debugging information
|
||||
void write_on(DebugInfoWriteStream* stream);
|
||||
|
||||
// Printing
|
||||
void print_on(outputStream* st) const;
|
||||
};
|
||||
|
||||
// An ObjectValue describes an object eliminated by escape analysis.
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ ConstantIntValue* CodeInstaller::_int_0_scope_value = new (ResourceObj::C_
|
|||
ConstantIntValue* CodeInstaller::_int_1_scope_value = new (ResourceObj::C_HEAP, mtJVMCI) ConstantIntValue(1);
|
||||
ConstantIntValue* CodeInstaller::_int_2_scope_value = new (ResourceObj::C_HEAP, mtJVMCI) ConstantIntValue(2);
|
||||
LocationValue* CodeInstaller::_illegal_value = new (ResourceObj::C_HEAP, mtJVMCI) LocationValue(Location());
|
||||
MarkerValue* CodeInstaller::_virtual_byte_array_marker = new (ResourceObj::C_HEAP, mtJVMCI) MarkerValue();
|
||||
|
||||
VMReg CodeInstaller::getVMRegFromLocation(JVMCIObject location, int total_frame_size, JVMCI_TRAPS) {
|
||||
if (location.is_null()) {
|
||||
|
@ -420,6 +421,7 @@ void CodeInstaller::record_object_value(ObjectValue* sv, JVMCIObject value, Grow
|
|||
int id = jvmci_env()->get_VirtualObject_id(value);
|
||||
Klass* klass = JVMCIENV->asKlass(type);
|
||||
bool isLongArray = klass == Universe::longArrayKlassObj();
|
||||
bool isByteArray = klass == Universe::byteArrayKlassObj();
|
||||
|
||||
JVMCIObjectArray values = jvmci_env()->get_VirtualObject_values(value);
|
||||
JVMCIObjectArray slotKinds = jvmci_env()->get_VirtualObject_slotKinds(value);
|
||||
|
@ -427,7 +429,24 @@ void CodeInstaller::record_object_value(ObjectValue* sv, JVMCIObject value, Grow
|
|||
ScopeValue* cur_second = NULL;
|
||||
JVMCIObject object = JVMCIENV->get_object_at(values, i);
|
||||
BasicType type = jvmci_env()->kindToBasicType(JVMCIENV->get_object_at(slotKinds, i), JVMCI_CHECK);
|
||||
ScopeValue* value = get_scope_value(object, type, objects, cur_second, JVMCI_CHECK);
|
||||
ScopeValue* value;
|
||||
if (JVMCIENV->equals(object, jvmci_env()->get_Value_ILLEGAL())) {
|
||||
if (isByteArray && type == T_ILLEGAL) {
|
||||
/*
|
||||
* The difference between a virtualized large access and a deferred write is the kind stored in the slotKinds
|
||||
* of the virtual object: in the virtualization case, the kind is illegal, in the deferred write case, the kind
|
||||
* is access stack kind (an int).
|
||||
*/
|
||||
value = _virtual_byte_array_marker;
|
||||
} else {
|
||||
value = _illegal_value;
|
||||
if (type == T_DOUBLE || type == T_LONG) {
|
||||
cur_second = _illegal_value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
value = get_scope_value(object, type, objects, cur_second, JVMCI_CHECK);
|
||||
}
|
||||
|
||||
if (isLongArray && cur_second == NULL) {
|
||||
// we're trying to put ints into a long array... this isn't really valid, but it's used for some optimizations.
|
||||
|
@ -435,6 +454,12 @@ void CodeInstaller::record_object_value(ObjectValue* sv, JVMCIObject value, Grow
|
|||
cur_second = _int_0_scope_value;
|
||||
}
|
||||
|
||||
if (isByteArray && cur_second != NULL && (type == T_DOUBLE || type == T_LONG)) {
|
||||
// we are trying to write a long in a byte Array. We will need to count the illegals to restore the type of
|
||||
// the thing we put inside.
|
||||
cur_second = NULL;
|
||||
}
|
||||
|
||||
if (cur_second != NULL) {
|
||||
sv->field_values()->append(cur_second);
|
||||
}
|
||||
|
|
|
@ -204,6 +204,7 @@ private:
|
|||
static ConstantIntValue* _int_1_scope_value;
|
||||
static ConstantIntValue* _int_2_scope_value;
|
||||
static LocationValue* _illegal_value;
|
||||
static MarkerValue* _virtual_byte_array_marker;
|
||||
|
||||
jint pd_next_offset(NativeInstruction* inst, jint pc_offset, JVMCIObject method, JVMCI_TRAPS);
|
||||
void pd_patch_OopConstant(int pc_offset, JVMCIObject constant, JVMCI_TRAPS);
|
||||
|
|
|
@ -541,6 +541,7 @@
|
|||
declare_constant(Deoptimization::Reason_unresolved) \
|
||||
declare_constant(Deoptimization::Reason_jsr_mismatch) \
|
||||
declare_constant(Deoptimization::Reason_LIMIT) \
|
||||
declare_constant(Deoptimization::_support_large_access_byte_array_virtualization) \
|
||||
\
|
||||
declare_constant(FieldInfo::access_flags_offset) \
|
||||
declare_constant(FieldInfo::name_index_offset) \
|
||||
|
|
|
@ -1032,6 +1032,80 @@ bool Deoptimization::realloc_objects(JavaThread* thread, frame* fr, RegisterMap*
|
|||
return failures;
|
||||
}
|
||||
|
||||
#if INCLUDE_JVMCI
|
||||
/**
|
||||
* For primitive types whose kind gets "erased" at runtime (shorts become stack ints),
|
||||
* we need to somehow be able to recover the actual kind to be able to write the correct
|
||||
* amount of bytes.
|
||||
* For that purpose, this method assumes that, for an entry spanning n bytes at index i,
|
||||
* the entries at index n + 1 to n + i are 'markers'.
|
||||
* For example, if we were writing a short at index 4 of a byte array of size 8, the
|
||||
* expected form of the array would be:
|
||||
*
|
||||
* {b0, b1, b2, b3, INT, marker, b6, b7}
|
||||
*
|
||||
* Thus, in order to get back the size of the entry, we simply need to count the number
|
||||
* of marked entries
|
||||
*
|
||||
* @param virtualArray the virtualized byte array
|
||||
* @param i index of the virtual entry we are recovering
|
||||
* @return The number of bytes the entry spans
|
||||
*/
|
||||
static int count_number_of_bytes_for_entry(ObjectValue *virtualArray, int i) {
|
||||
int index = i;
|
||||
while (++index < virtualArray->field_size() &&
|
||||
virtualArray->field_at(index)->is_marker()) {}
|
||||
return index - i;
|
||||
}
|
||||
|
||||
/**
|
||||
* If there was a guarantee for byte array to always start aligned to a long, we could
|
||||
* do a simple check on the parity of the index. Unfortunately, that is not always the
|
||||
* case. Thus, we check alignment of the actual address we are writing to.
|
||||
* In the unlikely case index 0 is 5-aligned for example, it would then be possible to
|
||||
* write a long to index 3.
|
||||
*/
|
||||
static jbyte* check_alignment_get_addr(typeArrayOop obj, int index, int expected_alignment) {
|
||||
jbyte* res = obj->byte_at_addr(index);
|
||||
assert((((intptr_t) res) % expected_alignment) == 0, "Non-aligned write");
|
||||
return res;
|
||||
}
|
||||
|
||||
static void byte_array_put(typeArrayOop obj, intptr_t val, int index, int byte_count) {
|
||||
switch (byte_count) {
|
||||
case 1:
|
||||
obj->byte_at_put(index, (jbyte) *((jint *) &val));
|
||||
break;
|
||||
case 2:
|
||||
*((jshort *) check_alignment_get_addr(obj, index, 2)) = (jshort) *((jint *) &val);
|
||||
break;
|
||||
case 4:
|
||||
*((jint *) check_alignment_get_addr(obj, index, 4)) = (jint) *((jint *) &val);
|
||||
break;
|
||||
case 8: {
|
||||
#ifdef _LP64
|
||||
jlong res = (jlong) *((jlong *) &val);
|
||||
#else
|
||||
#ifdef SPARC
|
||||
// For SPARC we have to swap high and low words.
|
||||
jlong v = (jlong) *((jlong *) &val);
|
||||
jlong res = 0;
|
||||
res |= ((v & (jlong) 0xffffffff) << 32);
|
||||
res |= ((v >> 32) & (jlong) 0xffffffff);
|
||||
#else
|
||||
jlong res = (jlong) *((jlong *) &val);
|
||||
#endif // SPARC
|
||||
#endif
|
||||
*((jlong *) check_alignment_get_addr(obj, index, 8)) = res;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
}
|
||||
#endif // INCLUDE_JVMCI
|
||||
|
||||
|
||||
// restore elements of an eliminated type array
|
||||
void Deoptimization::reassign_type_array_elements(frame* fr, RegisterMap* reg_map, ObjectValue* sv, typeArrayOop obj, BasicType type) {
|
||||
int index = 0;
|
||||
|
@ -1109,17 +1183,30 @@ void Deoptimization::reassign_type_array_elements(frame* fr, RegisterMap* reg_ma
|
|||
obj->char_at_put(index, (jchar)*((jint*)&val));
|
||||
break;
|
||||
|
||||
case T_BYTE:
|
||||
case T_BYTE: {
|
||||
assert(value->type() == T_INT, "Agreement.");
|
||||
// The value we get is erased as a regular int. We will need to find its actual byte count 'by hand'.
|
||||
val = value->get_int();
|
||||
#if INCLUDE_JVMCI
|
||||
int byte_count = count_number_of_bytes_for_entry(sv, i);
|
||||
byte_array_put(obj, val, index, byte_count);
|
||||
// According to byte_count contract, the values from i + 1 to i + byte_count are illegal values. Skip.
|
||||
i += byte_count - 1; // Balance the loop counter.
|
||||
index += byte_count;
|
||||
// index has been updated so continue at top of loop
|
||||
continue;
|
||||
#else
|
||||
obj->byte_at_put(index, (jbyte)*((jint*)&val));
|
||||
break;
|
||||
#endif // INCLUDE_JVMCI
|
||||
}
|
||||
|
||||
case T_BOOLEAN:
|
||||
case T_BOOLEAN: {
|
||||
assert(value->type() == T_INT, "Agreement.");
|
||||
val = value->get_int();
|
||||
obj->bool_at_put(index, (jboolean)*((jint*)&val));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
ShouldNotReachHere();
|
||||
|
@ -1128,7 +1215,6 @@ void Deoptimization::reassign_type_array_elements(frame* fr, RegisterMap* reg_ma
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// restore fields of an eliminated object array
|
||||
void Deoptimization::reassign_object_array_elements(frame* fr, RegisterMap* reg_map, ObjectValue* sv, objArrayOop obj) {
|
||||
for (int i = 0; i < sv->field_size(); i++) {
|
||||
|
|
|
@ -137,6 +137,11 @@ class Deoptimization : AllStatic {
|
|||
Unpack_LIMIT = 4
|
||||
};
|
||||
|
||||
#if INCLUDE_JVMCI
|
||||
// Can reconstruct virtualized unsafe large accesses to byte arrays.
|
||||
static const int _support_large_access_byte_array_virtualization = 1;
|
||||
#endif
|
||||
|
||||
// Make all nmethods that are marked_for_deoptimization not_entrant and deoptimize any live
|
||||
// activations using those nmethods. If an nmethod is passed as an argument then it is
|
||||
// marked_for_deoptimization and made not_entrant. Otherwise a scan of the code cache is done to
|
||||
|
|
|
@ -184,8 +184,10 @@ StackValue* StackValue::create_stack_value(const frame* fr, const RegisterMap* r
|
|||
} else if (sv->is_object()) { // Scalar replaced object in compiled frame
|
||||
Handle ov = ((ObjectValue *)sv)->value();
|
||||
return new StackValue(ov, (ov.is_null()) ? 1 : 0);
|
||||
} else if (sv->is_marker()) {
|
||||
// Should never need to directly construct a marker.
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
|
||||
// Unknown ScopeValue type
|
||||
ShouldNotReachHere();
|
||||
return new StackValue((intptr_t) 0); // dummy
|
||||
|
|
|
@ -190,6 +190,29 @@ public final class VirtualObject implements JavaValue {
|
|||
if (fieldIndex < fields.length) {
|
||||
throw new JVMCIError("Not enough values provided for fields in %s", this);
|
||||
}
|
||||
} else if (type.getComponentType().getJavaKind() == JavaKind.Byte) {
|
||||
for (int i = 0; i < values.length;) {
|
||||
JavaKind slotkind = slotKinds[i];
|
||||
if (slotkind != JavaKind.Byte) {
|
||||
if (!slotkind.isPrimitive()) {
|
||||
throw new JVMCIError("Storing a non-primitive in a byte array: %s %s", slotkind, toString());
|
||||
}
|
||||
int byteCount = 1;
|
||||
while (++i < values.length && slotKinds[i] == JavaKind.Illegal) {
|
||||
byteCount++;
|
||||
}
|
||||
/*
|
||||
* Checks: a) The byte count is a valid count (ie: power of two), b) if the kind
|
||||
* was not erased to int (happens for regular byte array accesses), check that
|
||||
* the count is correct, c) No writes spanning more than a long.
|
||||
*/
|
||||
if (!CodeUtil.isPowerOf2(byteCount) || (slotkind.getStackKind() != JavaKind.Int && byteCount != slotkind.getByteCount()) || byteCount > JavaKind.Long.getByteCount()) {
|
||||
throw new JVMCIError("Invalid number of illegals to reconstruct a byte array: %s in %s", byteCount, toString());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
|
||||
package org.graalvm.compiler.core.test;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.graalvm.compiler.api.directives.GraalDirectives;
|
||||
|
@ -34,6 +33,8 @@ import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
|
|||
import org.graalvm.compiler.nodes.extended.RawLoadNode;
|
||||
import org.graalvm.compiler.nodes.extended.RawStoreNode;
|
||||
import org.graalvm.compiler.nodes.spi.CoreProviders;
|
||||
import org.graalvm.compiler.nodes.virtual.VirtualArrayNode;
|
||||
import org.graalvm.compiler.nodes.virtual.VirtualObjectNode;
|
||||
import org.graalvm.compiler.options.OptionValues;
|
||||
import org.graalvm.compiler.phases.common.CanonicalizerPhase;
|
||||
import org.graalvm.compiler.virtual.phases.ea.PartialEscapePhase;
|
||||
|
@ -48,36 +49,6 @@ public class UnsafeVirtualizationTest extends GraalCompilerTest {
|
|||
|
||||
private static boolean[] FT = new boolean[]{false, true};
|
||||
|
||||
public static class Base {
|
||||
/*
|
||||
* This padding ensure that the size of the Base class ends up as a multiple of 8, which
|
||||
* makes the first field of the subclass 8-byte aligned.
|
||||
*/
|
||||
double padding;
|
||||
}
|
||||
|
||||
public static class A extends Base {
|
||||
int f1;
|
||||
int f2;
|
||||
}
|
||||
|
||||
private static final long AF1Offset;
|
||||
private static final long AF2Offset;
|
||||
static {
|
||||
long o1 = -1;
|
||||
long o2 = -1;
|
||||
try {
|
||||
Field f1 = A.class.getDeclaredField("f1");
|
||||
Field f2 = A.class.getDeclaredField("f2");
|
||||
o1 = UNSAFE.objectFieldOffset(f1);
|
||||
o2 = UNSAFE.objectFieldOffset(f2);
|
||||
} catch (NoSuchFieldException | SecurityException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
AF1Offset = o1;
|
||||
AF2Offset = o2;
|
||||
}
|
||||
|
||||
// Side effect to create a deopt point, after possible virtualization.
|
||||
static int sideEffectField;
|
||||
|
||||
|
@ -86,68 +57,68 @@ public class UnsafeVirtualizationTest extends GraalCompilerTest {
|
|||
}
|
||||
|
||||
public static int unsafeSnippet1(double i1, boolean c) {
|
||||
A a = new A();
|
||||
UNSAFE.putDouble(a, AF1Offset, i1);
|
||||
TestClassInt a = new TestClassInt();
|
||||
UNSAFE.putDouble(a, TestClassInt.fieldOffset1, i1);
|
||||
sideEffect();
|
||||
if (c) {
|
||||
GraalDirectives.deoptimize();
|
||||
}
|
||||
return UNSAFE.getInt(a, AF1Offset) + UNSAFE.getInt(a, AF2Offset);
|
||||
return UNSAFE.getInt(a, TestClassInt.fieldOffset1) + UNSAFE.getInt(a, TestClassInt.fieldOffset2);
|
||||
}
|
||||
|
||||
public static long unsafeSnippet2a(int i1, boolean c) {
|
||||
A a = new A();
|
||||
UNSAFE.putDouble(a, AF1Offset, i1);
|
||||
a.f1 = i1;
|
||||
TestClassInt a = new TestClassInt();
|
||||
UNSAFE.putDouble(a, TestClassInt.fieldOffset1, i1);
|
||||
a.setFirstField(i1);
|
||||
sideEffect();
|
||||
if (c) {
|
||||
GraalDirectives.deoptimize();
|
||||
}
|
||||
return UNSAFE.getLong(a, AF1Offset);
|
||||
return UNSAFE.getLong(a, TestClassInt.fieldOffset1);
|
||||
}
|
||||
|
||||
public static long unsafeSnippet2b(int i1, boolean c) {
|
||||
A a = new A();
|
||||
UNSAFE.putDouble(a, AF1Offset, i1);
|
||||
a.f2 = i1;
|
||||
TestClassInt a = new TestClassInt();
|
||||
UNSAFE.putDouble(a, TestClassInt.fieldOffset1, i1);
|
||||
a.setSecondField(i1);
|
||||
sideEffect();
|
||||
if (c) {
|
||||
GraalDirectives.deoptimize();
|
||||
}
|
||||
return UNSAFE.getLong(a, AF1Offset);
|
||||
return UNSAFE.getLong(a, TestClassInt.fieldOffset1);
|
||||
}
|
||||
|
||||
public static long unsafeSnippet3a(int i1, boolean c) {
|
||||
A a = new A();
|
||||
UNSAFE.putDouble(a, AF1Offset, i1);
|
||||
UNSAFE.putInt(a, AF1Offset, i1);
|
||||
TestClassInt a = new TestClassInt();
|
||||
UNSAFE.putDouble(a, TestClassInt.fieldOffset1, i1);
|
||||
UNSAFE.putInt(a, TestClassInt.fieldOffset1, i1);
|
||||
sideEffect();
|
||||
if (c) {
|
||||
GraalDirectives.deoptimize();
|
||||
}
|
||||
return UNSAFE.getLong(a, AF1Offset);
|
||||
return UNSAFE.getLong(a, TestClassInt.fieldOffset1);
|
||||
}
|
||||
|
||||
public static long unsafeSnippet3b(int i1, boolean c) {
|
||||
A a = new A();
|
||||
UNSAFE.putDouble(a, AF1Offset, i1);
|
||||
UNSAFE.putInt(a, AF2Offset, i1);
|
||||
TestClassInt a = new TestClassInt();
|
||||
UNSAFE.putDouble(a, TestClassInt.fieldOffset1, i1);
|
||||
UNSAFE.putInt(a, TestClassInt.fieldOffset2, i1);
|
||||
sideEffect();
|
||||
if (c) {
|
||||
GraalDirectives.deoptimize();
|
||||
}
|
||||
return UNSAFE.getLong(a, AF1Offset);
|
||||
return UNSAFE.getLong(a, TestClassInt.fieldOffset1);
|
||||
}
|
||||
|
||||
public static int unsafeSnippet4(double i1, boolean c) {
|
||||
A a = new A();
|
||||
UNSAFE.putDouble(a, AF1Offset, i1);
|
||||
UNSAFE.putDouble(a, AF1Offset, i1);
|
||||
TestClassInt a = new TestClassInt();
|
||||
UNSAFE.putDouble(a, TestClassInt.fieldOffset1, i1);
|
||||
UNSAFE.putDouble(a, TestClassInt.fieldOffset1, i1);
|
||||
sideEffect();
|
||||
if (c) {
|
||||
GraalDirectives.deoptimize();
|
||||
}
|
||||
return UNSAFE.getInt(a, AF1Offset) + UNSAFE.getInt(a, AF2Offset);
|
||||
return UNSAFE.getInt(a, TestClassInt.fieldOffset1) + UNSAFE.getInt(a, TestClassInt.fieldOffset2);
|
||||
}
|
||||
|
||||
public static int unsafeSnippet5(long i1, boolean c) {
|
||||
|
@ -514,17 +485,22 @@ public class UnsafeVirtualizationTest extends GraalCompilerTest {
|
|||
canonicalizer.apply(graph, context);
|
||||
}
|
||||
Result r = executeExpected(method, null, args);
|
||||
int readCount = 0;
|
||||
int writeCount = 0;
|
||||
boolean escapeReads = shouldEscapeRead && context.getPlatformConfigurationProvider().canVirtualizeLargeByteArrayAccess();
|
||||
boolean escapeWrites = shouldEscapeWrite && context.getPlatformConfigurationProvider().canVirtualizeLargeByteArrayAccess();
|
||||
if (escapeReads) {
|
||||
readCount = graph.getNodes().filter(RawLoadNode.class).count();
|
||||
}
|
||||
if (escapeWrites) {
|
||||
writeCount = graph.getNodes().filter(RawStoreNode.class).count();
|
||||
}
|
||||
int readCount = graph.getNodes().filter(RawLoadNode.class).count();
|
||||
int writeCount = graph.getNodes().filter(RawStoreNode.class).count();
|
||||
|
||||
new PartialEscapePhase(true, true, canonicalizer, null, options).apply(graph, context);
|
||||
|
||||
boolean canVirtualize = true;
|
||||
assert graph.getNodes().filter(VirtualObjectNode.class).count() == 1;
|
||||
VirtualObjectNode virtual = graph.getNodes().filter(VirtualObjectNode.class).first();
|
||||
if (virtual instanceof VirtualArrayNode) {
|
||||
VirtualArrayNode array = (VirtualArrayNode) virtual;
|
||||
if (array.isVirtualByteArray()) {
|
||||
canVirtualize = context.getPlatformConfigurationProvider().canVirtualizeLargeByteArrayAccess();
|
||||
}
|
||||
}
|
||||
boolean escapeReads = shouldEscapeRead && canVirtualize;
|
||||
boolean escapeWrites = shouldEscapeWrite && canVirtualize;
|
||||
if (escapeReads) {
|
||||
int newCount = graph.getNodes().filter(RawLoadNode.class).count();
|
||||
assertTrue(readCount > newCount, "PEA did not escape reads. before: " + readCount + ", after " + newCount);
|
||||
|
|
|
@ -96,7 +96,8 @@ public class EATestBase extends GraalCompilerTest {
|
|||
try {
|
||||
long localFieldOffset1 = UNSAFE.objectFieldOffset(EATestBase.TestClassInt.class.getField("x"));
|
||||
// Make the fields 8 byte aligned (Required for testing setLong on Architectures
|
||||
// which does not support unaligned memory access
|
||||
// which does not support unaligned memory access. The code has to be extra careful
|
||||
// because some JDKs do a better job of packing fields.
|
||||
if (localFieldOffset1 % 8 == 0) {
|
||||
fieldOffset1 = localFieldOffset1;
|
||||
fieldOffset2 = UNSAFE.objectFieldOffset(EATestBase.TestClassInt.class.getField("y"));
|
||||
|
|
|
@ -34,6 +34,8 @@ import jdk.vm.ci.runtime.JVMCI;
|
|||
|
||||
public abstract class VirtualObjectTestBase {
|
||||
|
||||
private static final int DEFAULT_BYTE_ARRAY_LENGTH = 50;
|
||||
|
||||
public static class SimpleObject {
|
||||
int i1;
|
||||
int i2;
|
||||
|
@ -50,7 +52,7 @@ public abstract class VirtualObjectTestBase {
|
|||
dummyValue = dummyValue | dummyValue << 32;
|
||||
if (kind.isNumericInteger()) {
|
||||
return JavaConstant.forIntegerKind(kind, dummyValue);
|
||||
} else if (kind == JavaKind.Float) {
|
||||
} else if (kind == JavaKind.Double) {
|
||||
return JavaConstant.forDouble(Double.longBitsToDouble(dummyValue));
|
||||
} else if (kind == JavaKind.Float) {
|
||||
return JavaConstant.forFloat(Float.intBitsToFloat((int) dummyValue));
|
||||
|
@ -117,4 +119,112 @@ public abstract class VirtualObjectTestBase {
|
|||
kinds[kinds.length - 1] = JavaKind.Int;
|
||||
test(simple, getJavaValues(kinds), kinds, true);
|
||||
}
|
||||
|
||||
public void testByteArray() {
|
||||
MetaAccessProvider metaAccess = JVMCI.getRuntime().getHostJVMCIBackend().getMetaAccess();
|
||||
|
||||
ResolvedJavaType byteArrayType = metaAccess.lookupJavaType(byte[].class);
|
||||
|
||||
JavaKind[] arrayKinds = new JavaKind[DEFAULT_BYTE_ARRAY_LENGTH];
|
||||
for (int i = 0; i < DEFAULT_BYTE_ARRAY_LENGTH; i++) {
|
||||
arrayKinds[i] = JavaKind.Byte;
|
||||
}
|
||||
|
||||
JavaKind[] kinds;
|
||||
JavaValue[] values;
|
||||
|
||||
// Generate a straighforward byte array.
|
||||
kinds = arrayKinds.clone();
|
||||
values = getJavaValues(kinds);
|
||||
test(byteArrayType, values, kinds, false);
|
||||
|
||||
// Write all primitive kinds into a byte array. Only primitives with enough Illegals should
|
||||
// succeed.
|
||||
for (JavaKind kind : JavaKind.values()) {
|
||||
if (kind.isPrimitive() && kind != JavaKind.Void) {
|
||||
kinds = arrayKinds.clone();
|
||||
kinds[0] = kind;
|
||||
for (int i = 1; i < kind.getByteCount() /* && i < DEFAULT_BYTE_ARRAY_LENGTH */; i++) {
|
||||
kinds[i] = JavaKind.Illegal;
|
||||
}
|
||||
values = getJavaValues(kinds);
|
||||
test(byteArrayType, values, kinds, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Write all kinds into a byte array, but forget to set Illegals.
|
||||
for (JavaKind kind : JavaKind.values()) {
|
||||
if (kind.isPrimitive() && kind != JavaKind.Void) {
|
||||
kinds = arrayKinds.clone();
|
||||
kinds[0] = kind;
|
||||
values = getJavaValues(kinds);
|
||||
test(byteArrayType, values, kinds, kind.getStackKind() != JavaKind.Int);
|
||||
}
|
||||
}
|
||||
|
||||
// Write all kinds into a byte array, but set a wrong number of Illegals.
|
||||
for (JavaKind kind : JavaKind.values()) {
|
||||
if (kind.isPrimitive() && kind != JavaKind.Void) {
|
||||
kinds = arrayKinds.clone();
|
||||
kinds[0] = kind;
|
||||
for (int i = 1; i < 5 /* && i < DEFAULT_BYTE_ARRAY_LENGTH */; i++) {
|
||||
kinds[i] = JavaKind.Illegal;
|
||||
}
|
||||
values = getJavaValues(kinds);
|
||||
test(byteArrayType, values, kinds, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Write a reference in a byte array.
|
||||
kinds = arrayKinds.clone();
|
||||
kinds[0] = JavaKind.Object;
|
||||
values = getJavaValues(kinds);
|
||||
test(byteArrayType, values, kinds, true);
|
||||
|
||||
// Put too many Illegals.
|
||||
kinds = arrayKinds.clone();
|
||||
for (int i = 1; i < DEFAULT_BYTE_ARRAY_LENGTH; i++) {
|
||||
kinds[i] = JavaKind.Illegal;
|
||||
}
|
||||
values = getJavaValues(kinds);
|
||||
test(byteArrayType, values, kinds, true);
|
||||
|
||||
// Fill a byte array with Illegals.
|
||||
kinds = arrayKinds.clone();
|
||||
for (int i = 0; i < DEFAULT_BYTE_ARRAY_LENGTH; i++) {
|
||||
kinds[i] = JavaKind.Illegal;
|
||||
}
|
||||
values = getJavaValues(kinds);
|
||||
test(byteArrayType, values, kinds, true);
|
||||
|
||||
// Write all kinds in a byte array successively.
|
||||
kinds = arrayKinds.clone();
|
||||
int i = 0;
|
||||
for (JavaKind kind : JavaKind.values()) {
|
||||
if (kind.isPrimitive() && kind != JavaKind.Void) {
|
||||
kinds[i] = kind;
|
||||
for (int j = 1; j < kind.getByteCount(); j++) {
|
||||
kinds[i + j] = JavaKind.Illegal;
|
||||
}
|
||||
i = i + kind.getByteCount();
|
||||
}
|
||||
}
|
||||
values = getJavaValues(kinds);
|
||||
test(byteArrayType, values, kinds, false);
|
||||
|
||||
// Write all kinds in a byte array successively, with an interleaving byte.
|
||||
kinds = arrayKinds.clone();
|
||||
i = 0;
|
||||
for (JavaKind kind : JavaKind.values()) {
|
||||
if (kind.isPrimitive() && kind != JavaKind.Void) {
|
||||
kinds[i] = kind;
|
||||
for (int j = 1; j < kind.getByteCount(); j++) {
|
||||
kinds[i + j] = JavaKind.Illegal;
|
||||
}
|
||||
i = i + kind.getByteCount() + 1;
|
||||
}
|
||||
}
|
||||
values = getJavaValues(kinds);
|
||||
test(byteArrayType, values, kinds, false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,4 +69,9 @@ public class VirtualObjectLayoutTest extends VirtualObjectTestBase {
|
|||
public void testFormat() {
|
||||
testBase();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testByteArrayFormat() {
|
||||
testByteArray();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue