Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
8254793: [JVMCI] improve speculation encoding
Reviewed-by: kvn, dlong, never
  • Loading branch information
Doug Simon committed Oct 19, 2020
1 parent 74ac77e commit f42c032
Show file tree
Hide file tree
Showing 9 changed files with 69 additions and 20 deletions.
7 changes: 4 additions & 3 deletions src/hotspot/share/jvmci/jvmciRuntime.cpp
Expand Up @@ -645,10 +645,11 @@ void JVMCINMethodData::initialize(
}

void JVMCINMethodData::add_failed_speculation(nmethod* nm, jlong speculation) {
uint index = (speculation >> 32) & 0xFFFFFFFF;
int length = (int) speculation;
jlong index = speculation >> JVMCINMethodData::SPECULATION_LENGTH_BITS;
guarantee(index >= 0 && index <= max_jint, "Encoded JVMCI speculation index is not a positive Java int: " INTPTR_FORMAT, index);
int length = speculation & JVMCINMethodData::SPECULATION_LENGTH_MASK;
if (index + length > (uint) nm->speculations_size()) {
fatal(INTPTR_FORMAT "[index: %d, length: %d] out of bounds wrt encoded speculations of length %u", speculation, index, length, nm->speculations_size());
fatal(INTPTR_FORMAT "[index: " JLONG_FORMAT ", length: %d out of bounds wrt encoded speculations of length %u", speculation, index, length, nm->speculations_size());
}
address data = nm->speculations_begin() + index;
FailedSpeculation::add_failed_speculation(nm, _failed_speculations, data, length);
Expand Down
9 changes: 9 additions & 0 deletions src/hotspot/share/jvmci/jvmciRuntime.hpp
Expand Up @@ -39,6 +39,7 @@ class MetadataHandles;
// JVMCINMethodData objects are inlined into nmethods
// at nmethod::_jvmci_data_offset.
class JVMCINMethodData {
friend class JVMCIVMStructs;
// Index for the HotSpotNmethod mirror in the nmethod's oops table.
// This is -1 if there is no mirror in the oops table.
int _nmethod_mirror_index;
Expand All @@ -51,6 +52,14 @@ class JVMCINMethodData {
// is appended when it causes a deoptimization.
FailedSpeculation** _failed_speculations;

// A speculation id is a length (low 5 bits) and an index into
// a jbyte array (i.e. 31 bits for a positive Java int).
enum {
// Keep in sync with HotSpotSpeculationEncoding.
SPECULATION_LENGTH_BITS = 5,
SPECULATION_LENGTH_MASK = (1 << SPECULATION_LENGTH_BITS) - 1
};

public:
// Computes the size of a JVMCINMethodData object
static int compute_size(const char* nmethod_mirror_name) {
Expand Down
1 change: 1 addition & 0 deletions src/hotspot/share/jvmci/vmStructs_jvmci.cpp
Expand Up @@ -391,6 +391,7 @@
declare_constant(HeapWordSize) \
declare_constant(InvocationEntryBci) \
declare_constant(LogKlassAlignmentInBytes) \
declare_constant(JVMCINMethodData::SPECULATION_LENGTH_BITS) \
\
declare_constant(JVM_ACC_WRITTEN_FLAGS) \
declare_constant(JVM_ACC_MONITOR_MATCH) \
Expand Down
3 changes: 2 additions & 1 deletion src/hotspot/share/runtime/thread.hpp
Expand Up @@ -1160,7 +1160,8 @@ class JavaThread: public Thread {
bool _in_retryable_allocation;

// An id of a speculation that JVMCI compiled code can use to further describe and
// uniquely identify the speculative optimization guarded by the uncommon trap
// uniquely identify the speculative optimization guarded by an uncommon trap.
// See JVMCINMethodData::SPECULATION_LENGTH_BITS for further details.
jlong _pending_failed_speculation;

// These fields are mutually exclusive in terms of live ranges.
Expand Down
Expand Up @@ -167,9 +167,9 @@ public boolean isSynthetic() {
}

/**
* Checks if this field has the {@link Stable} annotation.
* Checks if this field has the {@code Stable} annotation.
*
* @return true if field has {@link Stable} annotation, false otherwise
* @return true if field has {@code Stable} annotation, false otherwise
*/
@Override
public boolean isStable() {
Expand Down
Expand Up @@ -29,18 +29,33 @@
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

import jdk.vm.ci.common.JVMCIError;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaType;
import jdk.vm.ci.meta.SpeculationLog.SpeculationReasonEncoding;

/**
* Implements a {@link SpeculationReasonEncoding} that {@linkplain #getByteArray() produces} a byte
* array. Data is added via a {@link DataOutputStream}. When producing the final byte array, if the
* total length of data exceeds the length of a SHA-1 digest and a SHA-1 digest algorithm is
* available, then a SHA-1 digest of the data is produced instead.
* total length of data exceeds {@value HotSpotSpeculationEncoding#MAX_LENGTH}, then a SHA-1 digest
* of the data is produced instead.
*/
final class HotSpotSpeculationEncoding extends ByteArrayOutputStream implements SpeculationReasonEncoding {

/**
* Number of bits used for the length of an encoded speculation. The bit size of 5 is chosen to
* accommodate specifying // the length of a SHA-1 digest (i.e., 20 bytes).
*/
// Also defined in C++ JVMCINMethodData class - keep in sync.
static final int LENGTH_BITS = 5;

/**
* The maximum length of an encoded speculation.
*/
static final int MAX_LENGTH = (1 << LENGTH_BITS) - 1;

static final int LENGTH_MASK = MAX_LENGTH;

private DataOutputStream dos = new DataOutputStream(this);
private byte[] result;

Expand Down Expand Up @@ -160,7 +175,7 @@ private boolean addNull(Object o, int nullValue) {
* time.
*/
private static final boolean SHA1_IS_CLONEABLE;
private static final int SHA1_LENGTH;
private static final int SHA1_LENGTH = 20;

static {
MessageDigest sha1 = null;
Expand All @@ -171,13 +186,14 @@ private boolean addNull(Object o, int nullValue) {
sha1IsCloneable = true;
} catch (NoSuchAlgorithmException e) {
// Should never happen given that SHA-1 is mandated in a
// compliant Java platform implementation. However, be
// conservative and fall back to not using a digest.
// compliant Java platform implementation.
throw new JVMCIError(e);
} catch (CloneNotSupportedException e) {
}
SHA1 = sha1;
SHA1_IS_CLONEABLE = sha1IsCloneable;
SHA1_LENGTH = SHA1 == null ? 20 : SHA1.getDigestLength();
assert SHA1.getDigestLength() == SHA1_LENGTH;
assert SHA1_LENGTH < MAX_LENGTH;
}

/**
Expand All @@ -186,7 +202,7 @@ private boolean addNull(Object o, int nullValue) {
*/
byte[] getByteArray() {
if (result == null) {
if (SHA1 != null && count > SHA1_LENGTH) {
if (count > MAX_LENGTH) {
try {
MessageDigest md = SHA1_IS_CLONEABLE ? (MessageDigest) SHA1.clone() : MessageDigest.getInstance("SHA-1");
md.update(buf, 0, count);
Expand Down
Expand Up @@ -30,6 +30,7 @@
import java.util.List;

import jdk.vm.ci.code.BailoutException;
import jdk.vm.ci.common.JVMCIError;
import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.SpeculationLog;

Expand Down Expand Up @@ -119,9 +120,9 @@ public boolean managesFailedSpeculations() {
public static final class HotSpotSpeculation extends Speculation {

/**
* A speculation id is a long encoding an offset (high 32 bits) and a length (low 32 bts).
* Combined, the index and length denote where the {@linkplain #encoding encoded
* speculation} is in a {@linkplain HotSpotSpeculationLog#getFlattenedSpeculations
* A speculation id is a long encoding a length (low 5 bits) and an index into a
* {@code byte[]}. Combined, the index and length denote where the {@linkplain #encoding
* encoded speculation} is in a {@linkplain HotSpotSpeculationLog#getFlattenedSpeculations
* flattened} speculations array.
*/
private final JavaConstant id;
Expand Down Expand Up @@ -233,15 +234,21 @@ private static boolean contains(byte[][] haystack, int fromIndex, byte[] needle)
}

private static long encodeIndexAndLength(int index, int length) {
return ((long) index) << 32 | length;
if (length > HotSpotSpeculationEncoding.MAX_LENGTH || length < 0) {
throw new InternalError(String.format("Invalid encoded speculation length: %d (0x%x)", length, length));
}
if (index < 0) {
throw new JVMCIError("Encoded speculation index is negative: %d (0x%x)", index, index);
}
return (index << HotSpotSpeculationEncoding.LENGTH_BITS) | length;
}

private static int decodeIndex(long indexAndLength) {
return (int) (indexAndLength >>> 32);
return (int) (indexAndLength >>> HotSpotSpeculationEncoding.LENGTH_BITS);
}

private static int decodeLength(long indexAndLength) {
return (int) indexAndLength & 0xFFFFFFFF;
return (int) (indexAndLength & HotSpotSpeculationEncoding.LENGTH_MASK);
}

@Override
Expand Down Expand Up @@ -358,4 +365,3 @@ void doCleanup() {
final long address;
}
}

Expand Up @@ -25,6 +25,7 @@
import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.runtime;
import static jdk.vm.ci.hotspot.UnsafeAccess.UNSAFE;

import jdk.vm.ci.common.JVMCIError;
import jdk.vm.ci.services.Services;
import jdk.internal.misc.Unsafe;

Expand All @@ -46,6 +47,9 @@ static HotSpotVMConfig config() {

HotSpotVMConfig(HotSpotVMConfigStore store) {
super(store);

int speculationLengthBits = getConstant("JVMCINMethodData::SPECULATION_LENGTH_BITS", Integer.class);
JVMCIError.guarantee(HotSpotSpeculationEncoding.LENGTH_BITS == speculationLengthBits, "%d != %d", HotSpotSpeculationEncoding.LENGTH_BITS, speculationLengthBits);
}

/**
Expand Down
Expand Up @@ -25,6 +25,7 @@
* @test
* @requires vm.jvmci
* @modules jdk.internal.vm.ci/jdk.vm.ci.hotspot
* jdk.internal.vm.ci/jdk.vm.ci.runtime
* jdk.internal.vm.ci/jdk.vm.ci.meta
* @library /compiler/jvmci/jdk.vm.ci.hotspot.test/src
* @run testng/othervm
Expand All @@ -41,8 +42,12 @@
import org.testng.annotations.Test;

import jdk.vm.ci.hotspot.HotSpotSpeculationLog;
import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.MetaAccessProvider;
import jdk.vm.ci.meta.SpeculationLog;
import jdk.vm.ci.meta.SpeculationLog.SpeculationReasonEncoding;
import jdk.vm.ci.runtime.JVMCI;

public class TestHotSpotSpeculationLog {

Expand Down Expand Up @@ -88,6 +93,7 @@ public String toString() {

@Test
public synchronized void testFailedSpeculations() {
MetaAccessProvider metaAccess = JVMCI.getRuntime().getHostJVMCIBackend().getMetaAccess();
HotSpotSpeculationLog log = new HotSpotSpeculationLog();
DummyReason reason1 = new DummyReason("dummy1");
String longName = new String(new char[2000]).replace('\0', 'X');
Expand All @@ -98,6 +104,11 @@ public synchronized void testFailedSpeculations() {
SpeculationLog.Speculation s1 = log.speculate(reason1);
SpeculationLog.Speculation s2 = log.speculate(reason2);

JavaConstant encodedS1 = metaAccess.encodeSpeculation(s1);
JavaConstant encodedS2 = metaAccess.encodeSpeculation(s2);
Assert.assertEquals(JavaKind.Long, encodedS1.getJavaKind());
Assert.assertEquals(JavaKind.Long, encodedS2.getJavaKind());

boolean added = log.addFailedSpeculation(s1);
if (!added) {
throw new SkipException("log.addFailedSpeculation(s1) is false");
Expand Down

1 comment on commit f42c032

@bridgekeeper
Copy link

@bridgekeeper bridgekeeper bot commented on f42c032 Oct 19, 2020

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.