changeset 6791:a4c6e7e7efc5

Merge jdk7u281-ga
author Andrew John Hughes <gnu.andrew@redhat.com>
date Mon, 02 Nov 2020 02:26:23 +0000
parents 6de046544eec (current diff) ca0fd562f883 (diff)
children 77288aadc5a7
files .hgtags src/os/posix/vm/os_posix.cpp src/share/vm/classfile/classFileParser.cpp src/share/vm/classfile/systemDictionary.cpp src/share/vm/opto/loopTransform.cpp src/share/vm/opto/loopnode.hpp src/share/vm/opto/subnode.cpp src/share/vm/opto/type.cpp src/share/vm/prims/nativeLookup.cpp src/share/vm/runtime/advancedThresholdPolicy.cpp src/share/vm/runtime/globals.hpp src/share/vm/utilities/globalDefinitions.hpp
diffstat 15 files changed, 735 insertions(+), 405 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags	Sat Sep 26 19:05:33 2020 +0100
+++ b/.hgtags	Mon Nov 02 02:26:23 2020 +0000
@@ -971,3 +971,6 @@
 debb2913070604f611eaf916cbc90f024d17ef24 jdk7u271-ga
 2c74ba717122cba9979a05f7fb22bebc5ccbf543 icedtea-2.6.23
 2c74ba717122cba9979a05f7fb22bebc5ccbf543 icedtea-2.6.24pre00
+debb2913070604f611eaf916cbc90f024d17ef24 jdk7u281-b00
+e692f9f04b16a59a0959521d697bccff1a744160 jdk7u281-b01
+e692f9f04b16a59a0959521d697bccff1a744160 jdk7u281-ga
--- a/src/share/vm/classfile/altHashing.cpp	Sat Sep 26 19:05:33 2020 +0100
+++ b/src/share/vm/classfile/altHashing.cpp	Mon Nov 02 02:26:23 2020 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -22,12 +22,29 @@
  *
  */
 
+/*
+ * halfsiphash code adapted from reference implementation
+ * (https://github.com/veorq/SipHash/blob/master/halfsiphash.c)
+ * which is distributed with the following copyright:
+ *
+ * SipHash reference C implementation
+ *
+ * Copyright (c) 2016 Jean-Philippe Aumasson <jeanphilippe.aumasson@gmail.com>
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
 #include "precompiled.hpp"
 #include "classfile/altHashing.hpp"
-#include "classfile/symbolTable.hpp"
 #include "classfile/systemDictionary.hpp"
 #include "oops/markOop.hpp"
-#include "runtime/thread.hpp"
+#include "runtime/os.hpp"
 
 // Get the hash code of the classes mirror if it exists, otherwise just
 // return a random number, which is one of the possible hash code used for
@@ -39,266 +56,292 @@
 }
 
 // Seed value used for each alternative hash calculated.
-jint AltHashing::compute_seed() {
-  jlong nanos = os::javaTimeNanos();
-  jlong now = os::javaTimeMillis();
-  jint SEED_MATERIAL[8] = {
-            (jint) object_hash(SystemDictionary::String_klass()),
-            (jint) object_hash(SystemDictionary::System_klass()),
-            (jint) os::random(),  // current thread isn't a java thread
-            (jint) (((julong)nanos) >> 32),
-            (jint) nanos,
-            (jint) (((julong)now) >> 32),
-            (jint) now,
-            (jint) (os::javaTimeNanos() >> 2)
+uint64_t AltHashing::compute_seed() {
+  uint64_t nanos = os::javaTimeNanos();
+  uint64_t now = os::javaTimeMillis();
+  uint32_t SEED_MATERIAL[8] = {
+            (uint32_t) object_hash(SystemDictionary::String_klass()),
+            (uint32_t) object_hash(SystemDictionary::System_klass()),
+            (uint32_t) os::random(),  // current thread isn't a java thread
+            (uint32_t) (((uint64_t)nanos) >> 32),
+            (uint32_t) nanos,
+            (uint32_t) (((uint64_t)now) >> 32),
+            (uint32_t) now,
+            (uint32_t) (os::javaTimeNanos() >> 2)
   };
 
-  return murmur3_32(SEED_MATERIAL, 8);
+  return halfsiphash_64(SEED_MATERIAL, 8);
+}
+
+// utility function copied from java/lang/Integer
+static uint32_t Integer_rotateLeft(uint32_t i, int distance) {
+  return (i << distance) | (i >> (32 - distance));
+}
+
+static void halfsiphash_rounds(uint32_t v[4], int rounds) {
+  while (rounds-- > 0) {
+    v[0] += v[1];
+    v[1] = Integer_rotateLeft(v[1], 5);
+    v[1] ^= v[0];
+    v[0] = Integer_rotateLeft(v[0], 16);
+    v[2] += v[3];
+    v[3] = Integer_rotateLeft(v[3], 8);
+    v[3] ^= v[2];
+    v[0] += v[3];
+    v[3] = Integer_rotateLeft(v[3], 7);
+    v[3] ^= v[0];
+    v[2] += v[1];
+    v[1] = Integer_rotateLeft(v[1], 13);
+    v[1] ^= v[2];
+    v[2] = Integer_rotateLeft(v[2], 16);
+  }
+ }
+
+static void halfsiphash_adddata(uint32_t v[4], uint32_t newdata, int rounds) {
+  v[3] ^= newdata;
+  halfsiphash_rounds(v, rounds);
+  v[0] ^= newdata;
 }
 
+static void halfsiphash_init32(uint32_t v[4], uint64_t seed) {
+  v[0] = seed & 0xffffffff;
+  v[1] = seed >> 32;
+  v[2] = 0x6c796765 ^ v[0];
+  v[3] = 0x74656462 ^ v[1];
+}
 
-// Murmur3 hashing for Symbol
-jint AltHashing::murmur3_32(jint seed, const jbyte* data, int len) {
-  jint h1 = seed;
+static void halfsiphash_init64(uint32_t v[4], uint64_t seed) {
+  halfsiphash_init32(v, seed);
+  v[1] ^= 0xee;
+}
+
+uint32_t halfsiphash_finish32(uint32_t v[4], int rounds) {
+  v[2] ^= 0xff;
+  halfsiphash_rounds(v, rounds);
+  return (v[1] ^ v[3]);
+}
+
+static uint64_t halfsiphash_finish64(uint32_t v[4], int rounds) {
+  uint64_t rv;
+  v[2] ^= 0xee;
+  halfsiphash_rounds(v, rounds);
+  rv = v[1] ^ v[3];
+  v[1] ^= 0xdd;
+  halfsiphash_rounds(v, rounds);
+  rv |= (uint64_t)(v[1] ^ v[3]) << 32;
+  return rv;
+}
+
+// HalfSipHash-2-4 (32-bit output) for Symbols
+uint32_t AltHashing::halfsiphash_32(uint64_t seed, const uint8_t* data, int len) {
+  uint32_t v[4];
+  uint32_t newdata;
+  int off = 0;
   int count = len;
-  int offset = 0;
 
+  halfsiphash_init32(v, seed);
   // body
   while (count >= 4) {
-    jint k1 = (data[offset] & 0x0FF)
-        | (data[offset + 1] & 0x0FF) << 8
-        | (data[offset + 2] & 0x0FF) << 16
-        | data[offset + 3] << 24;
+    // Avoid sign extension with 0x0ff
+    newdata = (data[off] & 0x0FF)
+        | (data[off + 1] & 0x0FF) << 8
+        | (data[off + 2] & 0x0FF) << 16
+        | data[off + 3] << 24;
 
     count -= 4;
-    offset += 4;
+    off += 4;
 
-    k1 *= 0xcc9e2d51;
-    k1 = Integer_rotateLeft(k1, 15);
-    k1 *= 0x1b873593;
-
-    h1 ^= k1;
-    h1 = Integer_rotateLeft(h1, 13);
-    h1 = h1 * 5 + 0xe6546b64;
+    halfsiphash_adddata(v, newdata, 2);
   }
 
   // tail
+  newdata = ((uint32_t)len) << 24; // (Byte.SIZE / Byte.SIZE);
 
   if (count > 0) {
-    jint k1 = 0;
-
     switch (count) {
       case 3:
-        k1 ^= (data[offset + 2] & 0xff) << 16;
+        newdata |= (data[off + 2] & 0x0ff) << 16;
       // fall through
       case 2:
-        k1 ^= (data[offset + 1] & 0xff) << 8;
+        newdata |= (data[off + 1] & 0x0ff) << 8;
       // fall through
       case 1:
-        k1 ^= (data[offset] & 0xff);
+        newdata |= (data[off] & 0x0ff);
       // fall through
-      default:
-        k1 *= 0xcc9e2d51;
-        k1 = Integer_rotateLeft(k1, 15);
-        k1 *= 0x1b873593;
-        h1 ^= k1;
     }
   }
 
-  // finalization
-  h1 ^= len;
+  halfsiphash_adddata(v, newdata, 2);
 
-  // finalization mix force all bits of a hash block to avalanche
-  h1 ^= ((unsigned int)h1) >> 16;
-  h1 *= 0x85ebca6b;
-  h1 ^= ((unsigned int)h1) >> 13;
-  h1 *= 0xc2b2ae35;
-  h1 ^= ((unsigned int)h1) >> 16;
-
-  return h1;
+  // finalization
+  return halfsiphash_finish32(v, 4);
 }
 
-// Murmur3 hashing for Strings
-jint AltHashing::murmur3_32(jint seed, const jchar* data, int len) {
-  jint h1 = seed;
-
+// HalfSipHash-2-4 (32-bit output) for Strings
+uint32_t AltHashing::halfsiphash_32(uint64_t seed, const uint16_t* data, int len) {
+  uint32_t v[4];
+  uint32_t newdata;
   int off = 0;
   int count = len;
 
+  halfsiphash_init32(v, seed);
+
   // body
   while (count >= 2) {
-    jchar d1 = data[off++] & 0xFFFF;
-    jchar d2 = data[off++];
-    jint k1 = (d1 | d2 << 16);
+    uint16_t d1 = data[off++] & 0x0FFFF;
+    uint16_t d2 = data[off++];
+    newdata = (d1 | d2 << 16);
 
     count -= 2;
 
-    k1 *= 0xcc9e2d51;
-    k1 = Integer_rotateLeft(k1, 15);
-    k1 *= 0x1b873593;
-
-    h1 ^= k1;
-    h1 = Integer_rotateLeft(h1, 13);
-    h1 = h1 * 5 + 0xe6546b64;
+    halfsiphash_adddata(v, newdata, 2);
   }
 
   // tail
-
+  newdata = ((uint32_t)len * 2) << 24; // (Character.SIZE / Byte.SIZE);
   if (count > 0) {
-    int k1 = data[off];
-
-    k1 *= 0xcc9e2d51;
-    k1 = Integer_rotateLeft(k1, 15);
-    k1 *= 0x1b873593;
-    h1 ^= k1;
+    newdata |= (uint32_t)data[off];
   }
+  halfsiphash_adddata(v, newdata, 2);
 
   // finalization
-  h1 ^= len * 2; // (Character.SIZE / Byte.SIZE);
-
-  // finalization mix force all bits of a hash block to avalanche
-  h1 ^= ((unsigned int)h1) >> 16;
-  h1 *= 0x85ebca6b;
-  h1 ^= ((unsigned int)h1) >> 13;
-  h1 *= 0xc2b2ae35;
-  h1 ^= ((unsigned int)h1) >> 16;
-
-  return h1;
+  return halfsiphash_finish32(v, 4);
 }
 
-// Hash used for the seed.
-jint AltHashing::murmur3_32(jint seed, const int* data, int len) {
-  jint h1 = seed;
+// HalfSipHash-2-4 (64-bit output) for integers (used to create seed)
+uint64_t AltHashing::halfsiphash_64(uint64_t seed, const uint32_t* data, int len) {
+  uint32_t v[4];
 
   int off = 0;
   int end = len;
 
+  halfsiphash_init64(v, seed);
+
   // body
   while (off < end) {
-    jint k1 = data[off++];
-
-    k1 *= 0xcc9e2d51;
-    k1 = Integer_rotateLeft(k1, 15);
-    k1 *= 0x1b873593;
-
-    h1 ^= k1;
-    h1 = Integer_rotateLeft(h1, 13);
-    h1 = h1 * 5 + 0xe6546b64;
+    halfsiphash_adddata(v, (uint32_t)data[off++], 2);
   }
 
   // tail (always empty, as body is always 32-bit chunks)
 
   // finalization
-
-  h1 ^= len * 4; // (Integer.SIZE / Byte.SIZE);
-
-  // finalization mix force all bits of a hash block to avalanche
-  h1 ^= ((juint)h1) >> 16;
-  h1 *= 0x85ebca6b;
-  h1 ^= ((juint)h1) >> 13;
-  h1 *= 0xc2b2ae35;
-  h1 ^= ((juint)h1) >> 16;
-
-  return h1;
+  halfsiphash_adddata(v, ((uint32_t)len * 4) << 24, 2); // (Integer.SIZE / Byte.SIZE);
+  return halfsiphash_finish64(v, 4);
 }
 
-jint AltHashing::murmur3_32(const int* data, int len) {
-  return murmur3_32(0, data, len);
+// HalfSipHash-2-4 (64-bit output) for integers (used to create seed)
+uint64_t AltHashing::halfsiphash_64(const uint32_t* data, int len) {
+  return halfsiphash_64((uint64_t)0, data, len);
 }
 
 #ifndef PRODUCT
-// Overloaded versions for internal test.
-jint AltHashing::murmur3_32(const jbyte* data, int len) {
-  return murmur3_32(0, data, len);
-}
+  void AltHashing::testHalfsiphash_32_ByteArray() {
+    const int factor = 4;
 
-jint AltHashing::murmur3_32(const jchar* data, int len) {
-  return murmur3_32(0, data, len);
-}
+    uint8_t vector[256];
+    uint8_t hashes[factor * 256];
+
+    for (int i = 0; i < 256; i++) {
+      vector[i] = (uint8_t) i;
+    }
 
-// Internal test for alternate hashing.  Translated from JDK version
-// test/sun/misc/Hashing.java
-static const jbyte ONE_BYTE[] = { (jbyte) 0x80};
-static const jbyte TWO_BYTE[] = { (jbyte) 0x80, (jbyte) 0x81};
-static const jchar ONE_CHAR[] = { (jchar) 0x8180};
-static const jbyte THREE_BYTE[] = { (jbyte) 0x80, (jbyte) 0x81, (jbyte) 0x82};
-static const jbyte FOUR_BYTE[] = { (jbyte) 0x80, (jbyte) 0x81, (jbyte) 0x82, (jbyte) 0x83};
-static const jchar TWO_CHAR[] = { (jchar) 0x8180, (jchar) 0x8382};
-static const jint ONE_INT[] = { 0x83828180};
-static const jbyte SIX_BYTE[] = { (jbyte) 0x80, (jbyte) 0x81, (jbyte) 0x82, (jbyte) 0x83, (jbyte) 0x84, (jbyte) 0x85};
-static const jchar THREE_CHAR[] = { (jchar) 0x8180, (jchar) 0x8382, (jchar) 0x8584};
-static const jbyte EIGHT_BYTE[] = {
-  (jbyte) 0x80, (jbyte) 0x81, (jbyte) 0x82,
-  (jbyte) 0x83, (jbyte) 0x84, (jbyte) 0x85,
-  (jbyte) 0x86, (jbyte) 0x87};
-static const jchar FOUR_CHAR[] = {
-  (jchar) 0x8180, (jchar) 0x8382,
-  (jchar) 0x8584, (jchar) 0x8786};
+    // Hash subranges {}, {0}, {0,1}, {0,1,2}, ..., {0,...,255}
+    for (int i = 0; i < 256; i++) {
+      uint32_t hash = AltHashing::halfsiphash_32(256 - i, vector, i);
+      hashes[i * factor] = (uint8_t) hash;
+      hashes[i * factor + 1] = (uint8_t)(hash >> 8);
+      hashes[i * factor + 2] = (uint8_t)(hash >> 16);
+      hashes[i * factor + 3] = (uint8_t)(hash >> 24);
+    }
+
+    // hash to get const result.
+    uint32_t final_hash = AltHashing::halfsiphash_32(0, hashes, factor*256);
 
-static const jint TWO_INT[] = { 0x83828180, 0x87868584};
-
-static const juint MURMUR3_32_X86_CHECK_VALUE = 0xB0F57EE3;
+    // Value found using reference implementation for the hashes array.
+    //uint64_t k = 0;  // seed
+    //uint32_t reference;
+    //halfsiphash((const uint8_t*)hashes, factor*256, (const uint8_t *)&k, (uint8_t*)&reference, 4);
+    //printf("0x%x", reference);
 
-void AltHashing::testMurmur3_32_ByteArray() {
-  // printf("testMurmur3_32_ByteArray\n");
+    static const uint32_t HALFSIPHASH_32_BYTE_CHECK_VALUE = 0xd2be7fd8;
 
-  jbyte* vector = new jbyte[256];
-  jbyte* hashes = new jbyte[4 * 256];
-
-  for (int i = 0; i < 256; i++) {
-    vector[i] = (jbyte) i;
+    assert (HALFSIPHASH_32_BYTE_CHECK_VALUE == final_hash,
+      err_msg(
+          "Calculated hash result not as expected. Expected " UINT32_FORMAT " got " UINT32_FORMAT,
+          HALFSIPHASH_32_BYTE_CHECK_VALUE,
+          final_hash));
   }
 
-  // Hash subranges {}, {0}, {0,1}, {0,1,2}, ..., {0,...,255}
-  for (int i = 0; i < 256; i++) {
-    jint hash = murmur3_32(256 - i, vector, i);
-    hashes[i * 4] = (jbyte) hash;
-    hashes[i * 4 + 1] = (jbyte) (((juint)hash) >> 8);
-    hashes[i * 4 + 2] = (jbyte) (((juint)hash) >> 16);
-    hashes[i * 4 + 3] = (jbyte) (((juint)hash) >> 24);
+  void AltHashing::testHalfsiphash_32_CharArray() {
+    const int factor = 2;
+
+    uint16_t vector[256];
+    uint16_t hashes[factor * 256];
+
+    for (int i = 0; i < 256; i++) {
+      vector[i] = (uint16_t) i;
+    }
+
+    // Hash subranges {}, {0}, {0,1}, {0,1,2}, ..., {0,...,255}
+    for (int i = 0; i < 256; i++) {
+      uint32_t hash = AltHashing::halfsiphash_32(256 - i, vector, i);
+      hashes[i * factor] = (uint16_t) hash;
+      hashes[i * factor + 1] = (uint16_t)(hash >> 16);
+    }
+
+    // hash to get const result.
+    uint32_t final_hash = AltHashing::halfsiphash_32(0, hashes, factor*256);
+
+    // Value found using reference implementation for the hashes array.
+    //uint64_t k = 0;  // seed
+    //uint32_t reference;
+    //halfsiphash((const uint8_t*)hashes, 2*factor*256, (const uint8_t *)&k, (uint8_t*)&reference, 4);
+    //printf("0x%x", reference);
+
+    static const uint32_t HALFSIPHASH_32_CHAR_CHECK_VALUE = 0x428bf8a5;
+
+    assert(HALFSIPHASH_32_CHAR_CHECK_VALUE == final_hash,
+      err_msg(
+          "Calculated hash result not as expected. Expected " UINT32_FORMAT " got " UINT32_FORMAT,
+          HALFSIPHASH_32_CHAR_CHECK_VALUE,
+          final_hash));
   }
 
-  // hash to get const result.
-  juint final_hash = murmur3_32(hashes, 4*256);
-
-  assert (MURMUR3_32_X86_CHECK_VALUE == final_hash,
-    err_msg(
-        "Calculated hash result not as expected. Expected %08X got %08X\n",
-        MURMUR3_32_X86_CHECK_VALUE,
-        final_hash));
-}
+  // Test against sample hashes published with the reference implementation:
+  // https://github.com/veorq/SipHash
+  void AltHashing::testHalfsiphash_64_FromReference() {
 
-void AltHashing::testEquivalentHashes() {
-  jint jbytes, jchars, ints;
-
-  // printf("testEquivalentHashes\n");
-
-  jbytes = murmur3_32(TWO_BYTE, 2);
-  jchars = murmur3_32(ONE_CHAR, 1);
-  assert (jbytes == jchars,
-    err_msg("Hashes did not match. b:%08x != c:%08x\n", jbytes, jchars));
+    const uint64_t seed = 0x0706050403020100;
+    const uint64_t results[16] = {
+              0xc83cb8b9591f8d21, 0xa12ee55b178ae7d5,
+              0x8c85e4bc20e8feed, 0x99c7f5ae9f1fc77b,
+              0xb5f37b5fd2aa3673, 0xdba7ee6f0a2bf51b,
+              0xf1a63fae45107470, 0xb516001efb5f922d,
+              0x6c6211d8469d7028, 0xdc7642ec407ad686,
+              0x4caec8671cc8385b, 0x5ab1dc27adf3301e,
+              0x3e3ea94bc0a8eaa9, 0xe150f598795a4402,
+              0x1d5ff142f992a4a1, 0x60e426bf902876d6
+    };
+    uint32_t vector[16];
 
-  jbytes = murmur3_32(FOUR_BYTE, 4);
-  jchars = murmur3_32(TWO_CHAR, 2);
-  ints = murmur3_32(ONE_INT, 1);
-  assert ((jbytes == jchars) && (jbytes == ints),
-    err_msg("Hashes did not match. b:%08x != c:%08x != i:%08x\n", jbytes, jchars, ints));
-
-  jbytes = murmur3_32(SIX_BYTE, 6);
-  jchars = murmur3_32(THREE_CHAR, 3);
-  assert (jbytes == jchars,
-    err_msg("Hashes did not match. b:%08x != c:%08x\n", jbytes, jchars));
+    for (int i = 0; i < 16; i++)
+      vector[i] = 0x03020100 + i * 0x04040404;
 
-  jbytes = murmur3_32(EIGHT_BYTE, 8);
-  jchars = murmur3_32(FOUR_CHAR, 4);
-  ints = murmur3_32(TWO_INT, 2);
-  assert ((jbytes == jchars) && (jbytes == ints),
-    err_msg("Hashes did not match. b:%08x != c:%08x != i:%08x\n", jbytes, jchars, ints));
-}
+    for (int i = 0; i < 16; i++) {
+      uint64_t hash = AltHashing::halfsiphash_64(seed, vector, i);
+      assert(results[i] == hash,
+        err_msg(
+            "Calculated hash result not as expected. Round %d: "
+            "Expected " UINT64_FORMAT_X " got " UINT64_FORMAT_X "\n",
+            i,
+            results[i],
+            hash));
+    }
+  }
 
-// Returns true if the alternate hashcode is correct
 void AltHashing::test_alt_hash() {
-  testMurmur3_32_ByteArray();
-  testEquivalentHashes();
+  testHalfsiphash_32_ByteArray();
+  testHalfsiphash_32_CharArray();
+  testHalfsiphash_64_FromReference();
 }
 #endif // PRODUCT
--- a/src/share/vm/classfile/altHashing.hpp	Sat Sep 26 19:05:33 2020 +0100
+++ b/src/share/vm/classfile/altHashing.hpp	Mon Nov 02 02:26:23 2020 +0000
@@ -26,37 +26,30 @@
 #define SHARE_VM_CLASSFILE_ALTHASHING_HPP
 
 #include "prims/jni.h"
-#include "classfile/symbolTable.hpp"
+#include "memory/allocation.hpp"
 
 /**
- * Hashing utilities.
- *
- * Implementation of Murmur3 hashing.
- * This code was translated from src/share/classes/sun/misc/Hashing.java
- * code in the JDK.
+ * Implementation of alternate more secure hashing.
  */
 
 class AltHashing : AllStatic {
 
-  // utility function copied from java/lang/Integer
-  static jint Integer_rotateLeft(jint i, int distance) {
-    return (i << distance) | (((juint)i) >> (32-distance));
-  }
-  static jint murmur3_32(const int* data, int len);
-  static jint murmur3_32(jint seed, const int* data, int len);
+  // For the seed computation
+  static uint64_t halfsiphash_64(const uint32_t* data, int len);
+  static uint64_t halfsiphash_64(uint64_t seed, const uint32_t* data, int len);
+ #ifndef PRODUCT
+   // Hashing functions used for internal testing
+  static void testHalfsiphash_32_ByteArray();
+  static void testHalfsiphash_32_CharArray();
+  static void testHalfsiphash_64_FromReference();
+ #endif // PRODUCT
+ public:
+  static uint64_t compute_seed();
 
-#ifndef PRODUCT
-  // Hashing functions used for internal testing
-  static jint murmur3_32(const jbyte* data, int len);
-  static jint murmur3_32(const jchar* data, int len);
-  static void testMurmur3_32_ByteArray();
-  static void testEquivalentHashes();
-#endif // PRODUCT
-
- public:
-  static jint compute_seed();
-  static jint murmur3_32(jint seed, const jbyte* data, int len);
-  static jint murmur3_32(jint seed, const jchar* data, int len);
+  // For Symbols
+  static uint32_t halfsiphash_32(uint64_t seed, const uint8_t* data, int len);
+  // For Strings
+  static uint32_t halfsiphash_32(uint64_t seed, const uint16_t* data, int len);
   NOT_PRODUCT(static void test_alt_hash();)
 };
 #endif // SHARE_VM_CLASSFILE_ALTHASHING_HPP
--- a/src/share/vm/classfile/classFileParser.cpp	Sat Sep 26 19:05:33 2020 +0100
+++ b/src/share/vm/classfile/classFileParser.cpp	Mon Nov 02 02:26:23 2020 +0000
@@ -2556,6 +2556,80 @@
 // Inner classes can be static, private or protected (classic VM does this)
 #define RECOGNIZED_INNER_CLASS_MODIFIERS (JVM_RECOGNIZED_CLASS_MODIFIERS | JVM_ACC_PRIVATE | JVM_ACC_PROTECTED | JVM_ACC_STATIC)
 
+// Find index of the InnerClasses entry for the specified inner_class_info_index.
+// Return -1 if none is found.
+static int inner_classes_find_index(typeArrayHandle inner_classes, int inner, constantPoolHandle cp, int length) {
+  Symbol* cp_klass_name =  cp->klass_name_at(inner);
+  for (int idx = 0; idx < length; idx += instanceKlass::inner_class_next_offset) {
+    int idx_inner = inner_classes->ushort_at(idx + instanceKlass::inner_class_inner_class_info_offset);
+    if (cp->klass_name_at(idx_inner) == cp_klass_name) {
+      return idx;
+    }
+  }
+  return -1;
+}
+
+// Return the outer_class_info_index for the InnerClasses entry containing the
+// specified inner_class_info_index.  Return -1 if no InnerClasses entry is found.
+static int inner_classes_jump_to_outer(typeArrayHandle inner_classes, int inner, constantPoolHandle cp, int length) {
+  if (inner == 0) return -1;
+  int idx = inner_classes_find_index(inner_classes, inner, cp, length);
+  if (idx == -1) return -1;
+  int result = inner_classes->ushort_at(idx + instanceKlass::inner_class_outer_class_info_offset);
+  return result;
+}
+
+// Return true if circularity is found, false if no circularity is found.
+// Use Floyd's cycle finding algorithm.
+static bool inner_classes_check_loop_through_outer(typeArrayHandle inner_classes, int idx, constantPoolHandle cp, int length) {
+  int slow = inner_classes->ushort_at(idx + instanceKlass::inner_class_inner_class_info_offset);
+  int fast = inner_classes->ushort_at(idx + instanceKlass::inner_class_outer_class_info_offset);
+  while (fast != -1 && fast != 0) {
+    if (slow != 0 && (cp->klass_name_at(slow) == cp->klass_name_at(fast))) {
+      return true;  // found a circularity
+    }
+    fast = inner_classes_jump_to_outer(inner_classes, fast, cp, length);
+    if (fast == -1) return false;
+    fast = inner_classes_jump_to_outer(inner_classes, fast, cp, length);
+    if (fast == -1) return false;
+    slow = inner_classes_jump_to_outer(inner_classes, slow, cp, length);
+    assert(slow != -1, "sanity check");
+  }
+  return false;
+}
+
+// Loop through each InnerClasses entry checking for circularities and duplications
+// with other entries.  If duplicate entries are found then throw CFE.  Otherwise,
+// return true if a circularity or entries with duplicate inner_class_info_indexes
+// are found.
+bool ClassFileParser::check_inner_classes_circularity(constantPoolHandle cp, int length, TRAPS) {
+  // Loop through each InnerClasses entry.
+  for (int idx = 0; idx < length; idx += instanceKlass::inner_class_next_offset) {
+    // Return true if there are circular entries.
+    if (inner_classes_check_loop_through_outer(_inner_classes, idx, cp, length)) {
+      return true;
+    }
+    // Check if there are duplicate entries or entries with the same inner_class_info_index.
+    for (int y = idx + instanceKlass::inner_class_next_offset; y < length;
+         y += instanceKlass::inner_class_next_offset) {
+
+      // To maintain compatibility, throw an exception if duplicate inner classes
+      // entries are found.
+      guarantee_property((_inner_classes->ushort_at(idx) != _inner_classes->ushort_at(y) ||
+                          _inner_classes->ushort_at(idx+1) != _inner_classes->ushort_at(y+1) ||
+                          _inner_classes->ushort_at(idx+2) != _inner_classes->ushort_at(y+2) ||
+                          _inner_classes->ushort_at(idx+3) != _inner_classes->ushort_at(y+3)),
+                         "Duplicate entry in InnerClasses attribute in class file %s",
+                         CHECK_(true));
+      // Return true if there are two entries with the same inner_class_info_index.
+      if (_inner_classes->ushort_at(y) == _inner_classes->ushort_at(idx)) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
 // Return number of classes in the inner classes attribute table
 u2 ClassFileParser::parse_classfile_inner_classes_attribute(u1* inner_classes_attribute_start,
                                                             bool parsed_enclosingmethod_attribute,
@@ -2584,6 +2658,7 @@
   int size = length * 4 + (parsed_enclosingmethod_attribute ? 2 : 0);
   typeArrayOop ic = oopFactory::new_permanent_shortArray(size, CHECK_0);
   typeArrayHandle inner_classes(THREAD, ic);
+  set_class_inner_classes(inner_classes);
   int index = 0;
   int cp_size = cp->length();
   cfs->guarantee_more(8 * length, CHECK_0);  // 4-tuples of u2
@@ -2632,15 +2707,17 @@
   }
 
   // 4347400: make sure there's no duplicate entry in the classes array
+  bool has_circularity = false;
   if (_need_verify && _major_version >= JAVA_1_5_VERSION) {
-    for(int i = 0; i < length * 4; i += 4) {
-      for(int j = i + 4; j < length * 4; j += 4) {
-        guarantee_property((inner_classes->ushort_at(i)   != inner_classes->ushort_at(j) ||
-                            inner_classes->ushort_at(i+1) != inner_classes->ushort_at(j+1) ||
-                            inner_classes->ushort_at(i+2) != inner_classes->ushort_at(j+2) ||
-                            inner_classes->ushort_at(i+3) != inner_classes->ushort_at(j+3)),
-                            "Duplicate entry in InnerClasses in class file %s",
-                            CHECK_0);
+    has_circularity = check_inner_classes_circularity(cp, length * 4, CHECK_0);
+    if (has_circularity) {
+      // If circularity check failed then ignore InnerClasses attribute.
+      index = 0;
+      if (parsed_enclosingmethod_attribute) {
+        ic = oopFactory::new_permanent_shortArray(size, CHECK_0);
+        inner_classes = typeArrayHandle(THREAD, ic);
+      } else {
+        inner_classes = Universe::the_empty_short_array();
       }
     }
   }
@@ -2650,7 +2727,7 @@
     inner_classes->short_at_put(index++, enclosing_method_class_index);
     inner_classes->short_at_put(index++, enclosing_method_method_index);
   }
-  assert(index == size, "wrong size");
+  assert(index == size || has_circularity, "wrong size");
 
   // Update instanceKlass with inner class info.
   set_class_inner_classes(inner_classes);
--- a/src/share/vm/classfile/classFileParser.hpp	Sat Sep 26 19:05:33 2020 +0100
+++ b/src/share/vm/classfile/classFileParser.hpp	Mon Nov 02 02:26:23 2020 +0000
@@ -205,6 +205,10 @@
   u2 parse_generic_signature_attribute(constantPoolHandle cp, TRAPS);
   void parse_classfile_sourcefile_attribute(constantPoolHandle cp, TRAPS);
   void parse_classfile_source_debug_extension_attribute(constantPoolHandle cp, int length, TRAPS);
+
+  // Check for circularity in InnerClasses attribute.
+  bool check_inner_classes_circularity(constantPoolHandle cp, int length, TRAPS);
+
   u2   parse_classfile_inner_classes_attribute(u1* inner_classes_attribute_start,
                                                bool parsed_enclosingmethod_attribute,
                                                u2 enclosing_method_class_index,
--- a/src/share/vm/classfile/symbolTable.cpp	Sat Sep 26 19:05:33 2020 +0100
+++ b/src/share/vm/classfile/symbolTable.cpp	Mon Nov 02 02:26:23 2020 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -216,7 +216,7 @@
 // Pick hashing algorithm.
 unsigned int SymbolTable::hash_symbol(const char* s, int len) {
   return use_alternate_hashcode() ?
-           AltHashing::murmur3_32(seed(), (const jbyte*)s, len) :
+           AltHashing::halfsiphash_32(seed(), (const uint8_t*)s, len) :
            java_lang_String::to_hash(s, len);
 }
 
@@ -656,7 +656,7 @@
 
 // Pick hashing algorithm
 unsigned int StringTable::hash_string(const jchar* s, int len) {
-  return use_alternate_hashcode() ? AltHashing::murmur3_32(seed(), s, len) :
+  return use_alternate_hashcode() ? AltHashing::halfsiphash_32(seed(), s, len) :
                                     java_lang_String::to_hash(s, len);
 }
 
--- a/src/share/vm/classfile/systemDictionary.cpp	Sat Sep 26 19:05:33 2020 +0100
+++ b/src/share/vm/classfile/systemDictionary.cpp	Mon Nov 02 02:26:23 2020 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -1034,6 +1034,18 @@
   return k();
 }
 
+static bool is_prohibited_package_slow(Symbol* class_name) {
+  // Caller has ResourceMark
+  int length;
+  jchar* unicode = class_name->as_unicode(length);
+  return (length >= 5 &&
+          unicode[0] == 'j' &&
+          unicode[1] == 'a' &&
+          unicode[2] == 'v' &&
+          unicode[3] == 'a' &&
+          unicode[4] == '/');
+}
+
 // Add a klass to the system from a stream (called by jni_DefineClass and
 // JVM_DefineClass).
 // Note: class_name can be NULL. In that case we do not know the name of
@@ -1081,24 +1093,33 @@
   if (!HAS_PENDING_EXCEPTION &&
       !class_loader.is_null() &&
       parsed_name != NULL &&
-      parsed_name->utf8_length() >= (int)pkglen &&
-      !strncmp((const char*)parsed_name->bytes(), pkg, pkglen)) {
-    // It is illegal to define classes in the "java." package from
-    // JVM_DefineClass or jni_DefineClass unless you're the bootclassloader
+      parsed_name->utf8_length() >= (int)pkglen) {
     ResourceMark rm(THREAD);
-    char* name = parsed_name->as_C_string();
-    char* index = strrchr(name, '/');
-    assert(index != NULL, "must be");
-    *index = '\0'; // chop to just the package name
-    while ((index = strchr(name, '/')) != NULL) {
-      *index = '.'; // replace '/' with '.' in package name
+    bool prohibited;
+    const jbyte* base = parsed_name->base();
+    if ((base[0] | base[1] | base[2] | base[3] | base[4]) & 0x80) {
+      prohibited = is_prohibited_package_slow(parsed_name);
+    } else {
+      char* name = parsed_name->as_C_string();
+      prohibited = (strncmp(name, pkg, pkglen) == 0);
     }
-    const char* fmt = "Prohibited package name: %s";
-    size_t len = strlen(fmt) + strlen(name);
-    char* message = NEW_RESOURCE_ARRAY(char, len);
-    jio_snprintf(message, len, fmt, name);
-    Exceptions::_throw_msg(THREAD_AND_LOCATION,
-      vmSymbols::java_lang_SecurityException(), message);
+    if (prohibited) {
+      // It is illegal to define classes in the "java." package from
+      // JVM_DefineClass or jni_DefineClass unless you're the bootclassloader
+      char* name = parsed_name->as_C_string();
+      char* index = strrchr(name, '/');
+      assert(index != NULL, "must be");
+      *index = '\0'; // chop to just the package name
+      while ((index = strchr(name, '/')) != NULL) {
+        *index = '.'; // replace '/' with '.' in package name
+      }
+      const char* fmt = "Prohibited package name: %s";
+      size_t len = strlen(fmt) + strlen(name);
+      char* message = NEW_RESOURCE_ARRAY(char, len);
+      jio_snprintf(message, len, fmt, name);
+      Exceptions::_throw_msg(THREAD_AND_LOCATION,
+        vmSymbols::java_lang_SecurityException(), message);
+    }
   }
 
   if (!HAS_PENDING_EXCEPTION) {
--- a/src/share/vm/opto/addnode.cpp	Sat Sep 26 19:05:33 2020 +0100
+++ b/src/share/vm/opto/addnode.cpp	Mon Nov 02 02:26:23 2020 +0000
@@ -844,6 +844,14 @@
   return TypeInt::make( MAX2(r0->_lo,r1->_lo), MAX2(r0->_hi,r1->_hi), MAX2(r0->_widen,r1->_widen) );
 }
 
+// Check if addition of an integer with type 't' and a constant 'c' can overflow
+static bool can_overflow(const TypeInt* t, jint c) {
+  jint t_lo = t->_lo;
+  jint t_hi = t->_hi;
+  return ((c < 0 && (java_add(t_lo, c) > t_lo)) ||
+          (c > 0 && (java_add(t_hi, c) < t_hi)));
+}
+
 //=============================================================================
 //------------------------------Idealize---------------------------------------
 // MINs show up in range-check loop limit calculations.  Look for
@@ -866,7 +874,7 @@
 
   // Get left input & constant
   Node *x = l;
-  int x_off = 0;
+  jint x_off = 0;
   if( x->Opcode() == Op_AddI && // Check for "x+c0" and collect constant
       x->in(2)->is_Con() ) {
     const Type *t = x->in(2)->bottom_type();
@@ -877,7 +885,7 @@
 
   // Scan a right-spline-tree for MINs
   Node *y = r;
-  int y_off = 0;
+  jint y_off = 0;
   // Check final part of MIN tree
   if( y->Opcode() == Op_AddI && // Check for "y+c1" and collect constant
       y->in(2)->is_Con() ) {
@@ -891,6 +899,7 @@
     return this;
   }
 
+  const TypeInt* tx = phase->type(x)->isa_int();
 
   if( r->Opcode() == Op_MinI ) {
     assert( r != r->in(2), "dead loop in MinINode::Ideal" );
@@ -907,18 +916,23 @@
     if( x->_idx > y->_idx )
       return new (phase->C) MinINode(r->in(1),phase->transform(new (phase->C) MinINode(l,r->in(2))));
 
-    // See if covers: MIN2(x+c0,MIN2(y+c1,z))
-    if( !phase->eqv(x,y) ) return NULL;
-    // If (y == x) transform MIN2(x+c0, MIN2(x+c1,z)) into
-    // MIN2(x+c0 or x+c1 which less, z).
-    return new (phase->C) MinINode(phase->transform(new (phase->C) AddINode(x,phase->intcon(MIN2(x_off,y_off)))),r->in(2));
+    // Transform MIN2(x + c0, MIN2(x + c1, z)) into MIN2(x + MIN2(c0, c1), z)
+    // if x == y and the additions can't overflow.
+    if (phase->eqv(x,y) &&
+        !can_overflow(tx, x_off) &&
+        !can_overflow(tx, y_off)) {
+      return new (phase->C) MinINode(phase->transform(new (phase->C) AddINode(x, phase->intcon(MIN2(x_off, y_off)))), r->in(2));
+    }
   } else {
-    // See if covers: MIN2(x+c0,y+c1)
-    if( !phase->eqv(x,y) ) return NULL;
-    // If (y == x) transform MIN2(x+c0,x+c1) into x+c0 or x+c1 which less.
-    return new (phase->C) AddINode(x,phase->intcon(MIN2(x_off,y_off)));
+    // Transform MIN2(x + c0, y + c1) into x + MIN2(c0, c1)
+    // if x == y and the additions can't overflow.
+    if (phase->eqv(x,y) &&
+        !can_overflow(tx, x_off) &&
+        !can_overflow(tx, y_off)) {
+      return new (phase->C) AddINode(x,phase->intcon(MIN2(x_off,y_off)));
+    }
   }
-
+  return NULL;
 }
 
 //------------------------------add_ring---------------------------------------
--- a/src/share/vm/opto/loopTransform.cpp	Sat Sep 26 19:05:33 2020 +0100
+++ b/src/share/vm/opto/loopTransform.cpp	Mon Nov 02 02:26:23 2020 +0000
@@ -1517,58 +1517,78 @@
 }
 
 //------------------------------adjust_limit-----------------------------------
-// Helper function for add_constraint().
-Node* PhaseIdealLoop::adjust_limit(int stride_con, Node * scale, Node *offset, Node *rc_limit, Node *loop_limit, Node *pre_ctrl) {
-  // Compute "I :: (limit-offset)/scale"
-  Node *con = new (C) SubINode(rc_limit, offset);
-  register_new_node(con, pre_ctrl);
-  Node *X = new (C) DivINode(0, con, scale);
-  register_new_node(X, pre_ctrl);
+// Helper function that computes new loop limit as (rc_limit-offset)/scale
+Node* PhaseIdealLoop::adjust_limit(bool is_positive_stride, Node* scale, Node* offset, Node* rc_limit, Node* old_limit, Node* pre_ctrl, bool round) {
+  Node* sub = new (C) SubLNode(rc_limit, offset);
+  register_new_node(sub, pre_ctrl);
+  Node* limit = new (C) DivLNode(NULL, sub, scale);
+  register_new_node(limit, pre_ctrl);
+
+  // When the absolute value of scale is greater than one, the division
+  // may round limit down/up, so add/sub one to/from the limit.
+  if (round) {
+    limit = new (C) AddLNode(limit, _igvn.longcon(is_positive_stride ? -1 : 1));
+    register_new_node(limit, pre_ctrl);
+  }
 
-  // Adjust loop limit
-  loop_limit = (stride_con > 0)
-               ? (Node*)(new (C) MinINode(loop_limit, X))
-               : (Node*)(new (C) MaxINode(loop_limit, X));
-  register_new_node(loop_limit, pre_ctrl);
-  return loop_limit;
+  // Clamp the limit to handle integer under-/overflows.
+  // When reducing the limit, clamp to [min_jint, old_limit]:
+  //   MIN(old_limit, MAX(limit, min_jint))
+  // When increasing the limit, clamp to [old_limit, max_jint]:
+  //   MAX(old_limit, MIN(limit, max_jint))
+  Node* cmp = new (C) CmpLNode(limit, _igvn.longcon(is_positive_stride ? min_jint : max_jint));
+  register_new_node(cmp, pre_ctrl);
+  Node* bol = new (C) BoolNode(cmp, is_positive_stride ? BoolTest::lt : BoolTest::gt);
+  register_new_node(bol, pre_ctrl);
+  limit = new (C) ConvL2INode(limit);
+  register_new_node(limit, pre_ctrl);
+  limit = new (C) CMoveINode(bol, limit, _igvn.intcon(is_positive_stride ? min_jint : max_jint), TypeInt::INT);
+  register_new_node(limit, pre_ctrl);
+
+  limit = is_positive_stride ? (Node*)(new (C) MinINode(old_limit, limit))
+                             : (Node*)(new (C) MaxINode(old_limit, limit));
+  register_new_node(limit, pre_ctrl);
+  return limit;
 }
 
 //------------------------------add_constraint---------------------------------
 // Constrain the main loop iterations so the conditions:
-//    low_limit <= scale_con * I + offset  <  upper_limit
-// always holds true.  That is, either increase the number of iterations in
-// the pre-loop or the post-loop until the condition holds true in the main
-// loop.  Stride, scale, offset and limit are all loop invariant.  Further,
-// stride and scale are constants (offset and limit often are).
-void PhaseIdealLoop::add_constraint( int stride_con, int scale_con, Node *offset, Node *low_limit, Node *upper_limit, Node *pre_ctrl, Node **pre_limit, Node **main_limit ) {
-  // For positive stride, the pre-loop limit always uses a MAX function
-  // and the main loop a MIN function.  For negative stride these are
-  // reversed.
+//    low_limit <= scale_con*I + offset < upper_limit
+// always hold true. That is, either increase the number of iterations in the
+// pre-loop or reduce the number of iterations in the main-loop until the condition
+// holds true in the main-loop. Stride, scale, offset and limit are all loop
+// invariant. Further, stride and scale are constants (offset and limit often are).
+void PhaseIdealLoop::add_constraint(jlong stride_con, jlong scale_con, Node* offset, Node* low_limit, Node* upper_limit, Node* pre_ctrl, Node** pre_limit, Node** main_limit) {
+  assert(_igvn.type(offset)->isa_long() != NULL && _igvn.type(low_limit)->isa_long() != NULL &&
+         _igvn.type(upper_limit)->isa_long() != NULL, "arguments should be long values");
 
-  // Also for positive stride*scale the affine function is increasing, so the
-  // pre-loop must check for underflow and the post-loop for overflow.
-  // Negative stride*scale reverses this; pre-loop checks for overflow and
-  // post-loop for underflow.
+  // For a positive stride, we need to reduce the main-loop limit and
+  // increase the pre-loop limit. This is reversed for a negative stride.
+  bool is_positive_stride = (stride_con > 0);
 
-  Node *scale = _igvn.intcon(scale_con);
+  // If the absolute scale value is greater one, division in 'adjust_limit' may require
+  // rounding. Make sure the ABS method correctly handles min_jint.
+  // Only do this for the pre-loop, one less iteration of the main loop doesn't hurt.
+  bool round = ABS(scale_con) > 1;
+
+  Node* scale = _igvn.longcon(scale_con);
   set_ctrl(scale, C->root());
 
   if ((stride_con^scale_con) >= 0) { // Use XOR to avoid overflow
+    // Positive stride*scale: the affine function is increasing,
+    // the pre-loop checks for underflow and the post-loop for overflow.
+
     // The overflow limit: scale*I+offset < upper_limit
-    // For main-loop compute
+    // For the main-loop limit compute:
     //   ( if (scale > 0) /* and stride > 0 */
     //       I < (upper_limit-offset)/scale
     //     else /* scale < 0 and stride < 0 */
     //       I > (upper_limit-offset)/scale
     //   )
-    //
-    // (upper_limit-offset) may overflow or underflow.
-    // But it is fine since main loop will either have
-    // less iterations or will be skipped in such case.
-    *main_limit = adjust_limit(stride_con, scale, offset, upper_limit, *main_limit, pre_ctrl);
+    *main_limit = adjust_limit(is_positive_stride, scale, offset, upper_limit, *main_limit, pre_ctrl, false);
 
-    // The underflow limit: low_limit <= scale*I+offset.
-    // For pre-loop compute
+    // The underflow limit: low_limit <= scale*I+offset
+    // For the pre-loop limit compute:
     //   NOT(scale*I+offset >= low_limit)
     //   scale*I+offset < low_limit
     //   ( if (scale > 0) /* and stride > 0 */
@@ -1576,39 +1596,13 @@
     //     else /* scale < 0 and stride < 0 */
     //       I > (low_limit-offset)/scale
     //   )
+    *pre_limit = adjust_limit(!is_positive_stride, scale, offset, low_limit, *pre_limit, pre_ctrl, round);
+  } else {
+    // Negative stride*scale: the affine function is decreasing,
+    // the pre-loop checks for overflow and the post-loop for underflow.
 
-    if (low_limit->get_int() == -max_jint) {
-      if (!RangeLimitCheck) return;
-      // We need this guard when scale*pre_limit+offset >= limit
-      // due to underflow. So we need execute pre-loop until
-      // scale*I+offset >= min_int. But (min_int-offset) will
-      // underflow when offset > 0 and X will be > original_limit
-      // when stride > 0. To avoid it we replace positive offset with 0.
-      //
-      // Also (min_int+1 == -max_int) is used instead of min_int here
-      // to avoid problem with scale == -1 (min_int/(-1) == min_int).
-      Node* shift = _igvn.intcon(31);
-      set_ctrl(shift, C->root());
-      Node* sign = new (C) RShiftINode(offset, shift);
-      register_new_node(sign, pre_ctrl);
-      offset = new (C) AndINode(offset, sign);
-      register_new_node(offset, pre_ctrl);
-    } else {
-      assert(low_limit->get_int() == 0, "wrong low limit for range check");
-      // The only problem we have here when offset == min_int
-      // since (0-min_int) == min_int. It may be fine for stride > 0
-      // but for stride < 0 X will be < original_limit. To avoid it
-      // max(pre_limit, original_limit) is used in do_range_check().
-    }
-    // Pass (-stride) to indicate pre_loop_cond = NOT(main_loop_cond);
-    *pre_limit = adjust_limit((-stride_con), scale, offset, low_limit, *pre_limit, pre_ctrl);
-
-  } else { // stride_con*scale_con < 0
-    // For negative stride*scale pre-loop checks for overflow and
-    // post-loop for underflow.
-    //
     // The overflow limit: scale*I+offset < upper_limit
-    // For pre-loop compute
+    // For the pre-loop limit compute:
     //   NOT(scale*I+offset < upper_limit)
     //   scale*I+offset >= upper_limit
     //   scale*I+offset+1 > upper_limit
@@ -1617,56 +1611,24 @@
     //     else /* scale > 0 and stride < 0 */
     //       I > (upper_limit-(offset+1))/scale
     //   )
-    //
-    // (upper_limit-offset-1) may underflow or overflow.
-    // To avoid it min(pre_limit, original_limit) is used
-    // in do_range_check() for stride > 0 and max() for < 0.
-    Node *one  = _igvn.intcon(1);
+    Node* one = _igvn.longcon(1);
     set_ctrl(one, C->root());
-
-    Node *plus_one = new (C) AddINode(offset, one);
+    Node* plus_one = new (C) AddLNode(offset, one);
     register_new_node( plus_one, pre_ctrl );
-    // Pass (-stride) to indicate pre_loop_cond = NOT(main_loop_cond);
-    *pre_limit = adjust_limit((-stride_con), scale, plus_one, upper_limit, *pre_limit, pre_ctrl);
+    *pre_limit = adjust_limit(!is_positive_stride, scale, plus_one, upper_limit, *pre_limit, pre_ctrl, round);
 
-    if (low_limit->get_int() == -max_jint) {
-      if (!RangeLimitCheck) return;
-      // We need this guard when scale*main_limit+offset >= limit
-      // due to underflow. So we need execute main-loop while
-      // scale*I+offset+1 > min_int. But (min_int-offset-1) will
-      // underflow when (offset+1) > 0 and X will be < main_limit
-      // when scale < 0 (and stride > 0). To avoid it we replace
-      // positive (offset+1) with 0.
-      //
-      // Also (min_int+1 == -max_int) is used instead of min_int here
-      // to avoid problem with scale == -1 (min_int/(-1) == min_int).
-      Node* shift = _igvn.intcon(31);
-      set_ctrl(shift, C->root());
-      Node* sign = new (C) RShiftINode(plus_one, shift);
-      register_new_node(sign, pre_ctrl);
-      plus_one = new (C) AndINode(plus_one, sign);
-      register_new_node(plus_one, pre_ctrl);
-    } else {
-      assert(low_limit->get_int() == 0, "wrong low limit for range check");
-      // The only problem we have here when offset == max_int
-      // since (max_int+1) == min_int and (0-min_int) == min_int.
-      // But it is fine since main loop will either have
-      // less iterations or will be skipped in such case.
-    }
-    // The underflow limit: low_limit <= scale*I+offset.
-    // For main-loop compute
+    // The underflow limit: low_limit <= scale*I+offset
+    // For the main-loop limit compute:
     //   scale*I+offset+1 > low_limit
     //   ( if (scale < 0) /* and stride > 0 */
     //       I < (low_limit-(offset+1))/scale
     //     else /* scale > 0 and stride < 0 */
     //       I > (low_limit-(offset+1))/scale
     //   )
-
-    *main_limit = adjust_limit(stride_con, scale, plus_one, low_limit, *main_limit, pre_ctrl);
+    *main_limit = adjust_limit(is_positive_stride, scale, plus_one, low_limit, *main_limit, pre_ctrl, false);
   }
 }
 
-
 //------------------------------is_scaled_iv---------------------------------
 // Return true if exp is a constant times an induction var
 bool PhaseIdealLoop::is_scaled_iv(Node* exp, Node* iv, int* p_scale) {
@@ -1826,22 +1788,14 @@
   // Must know if its a count-up or count-down loop
 
   int stride_con = cl->stride_con();
-  Node *zero = _igvn.intcon(0);
-  Node *one  = _igvn.intcon(1);
+  Node* zero = _igvn.longcon(0);
+  Node* one  = _igvn.longcon(1);
   // Use symmetrical int range [-max_jint,max_jint]
-  Node *mini = _igvn.intcon(-max_jint);
+  Node* mini = _igvn.longcon(-max_jint);
   set_ctrl(zero, C->root());
   set_ctrl(one,  C->root());
   set_ctrl(mini, C->root());
 
-  // Range checks that do not dominate the loop backedge (ie.
-  // conditionally executed) can lengthen the pre loop limit beyond
-  // the original loop limit. To prevent this, the pre limit is
-  // (for stride > 0) MINed with the original loop limit (MAXed
-  // stride < 0) when some range_check (rc) is conditionally
-  // executed.
-  bool conditional_rc = false;
-
   // Check loop body for tests of trip-counter plus loop-invariant vs
   // loop-invariant.
   for( uint i = 0; i < loop->_body.size(); i++ ) {
@@ -1920,15 +1874,20 @@
       // stride_con and scale_con can be negative which will flip about the
       // sense of the test.
 
+      // Perform the limit computations in jlong to avoid overflow
+      jlong lscale_con = scale_con;
+      Node* int_offset = offset;
+      offset = new (C) ConvI2LNode(offset);
+      register_new_node(offset, pre_ctrl);
+      Node* int_limit = limit;
+      limit = new (C) ConvI2LNode(limit);
+      register_new_node(limit, pre_ctrl);
+
       // Adjust pre and main loop limits to guard the correct iteration set
       if( cmp->Opcode() == Op_CmpU ) {// Unsigned compare is really 2 tests
         if( b_test._test == BoolTest::lt ) { // Range checks always use lt
           // The underflow and overflow limits: 0 <= scale*I+offset < limit
-          add_constraint( stride_con, scale_con, offset, zero, limit, pre_ctrl, &pre_limit, &main_limit );
-          if (!conditional_rc) {
-            // (0-offset)/scale could be outside of loop iterations range.
-            conditional_rc = !loop->dominates_backedge(iff) || RangeLimitCheck;
-          }
+          add_constraint(stride_con, lscale_con, offset, zero, limit, pre_ctrl, &pre_limit, &main_limit);
         } else {
 #ifndef PRODUCT
           if( PrintOpto )
@@ -1942,16 +1901,16 @@
           // Fall into GE case
         case BoolTest::ge:
           // Convert (I*scale+offset) >= Limit to (I*(-scale)+(-offset)) <= -Limit
-          scale_con = -scale_con;
-          offset = new (C) SubINode( zero, offset );
+          lscale_con = -lscale_con;
+          offset = new (C) SubLNode(zero, offset);
           register_new_node( offset, pre_ctrl );
-          limit  = new (C) SubINode( zero, limit  );
+          limit  = new (C) SubLNode(zero, limit);
           register_new_node( limit, pre_ctrl );
           // Fall into LE case
         case BoolTest::le:
           if (b_test._test != BoolTest::gt) {
             // Convert X <= Y to X < Y+1
-            limit = new (C) AddINode( limit, one );
+            limit = new (C) AddLNode(limit, one);
             register_new_node( limit, pre_ctrl );
           }
           // Fall into LT case
@@ -1959,13 +1918,7 @@
           // The underflow and overflow limits: MIN_INT <= scale*I+offset < limit
           // Note: (MIN_INT+1 == -MAX_INT) is used instead of MIN_INT here
           // to avoid problem with scale == -1: MIN_INT/(-1) == MIN_INT.
-          add_constraint( stride_con, scale_con, offset, mini, limit, pre_ctrl, &pre_limit, &main_limit );
-          if (!conditional_rc) {
-            // ((MIN_INT+1)-offset)/scale could be outside of loop iterations range.
-            // Note: negative offset is replaced with 0 but (MIN_INT+1)/scale could
-            // still be outside of loop range.
-            conditional_rc = !loop->dominates_backedge(iff) || RangeLimitCheck;
-          }
+          add_constraint(stride_con, lscale_con, offset, mini, limit, pre_ctrl, &pre_limit, &main_limit);
           break;
         default:
 #ifndef PRODUCT
@@ -2001,7 +1954,8 @@
   }
 
   // Update loop limits
-  if (conditional_rc) {
+  if (pre_limit != orig_limit) {
+    // Computed pre-loop limit can be outside of loop iterations range.
     pre_limit = (stride_con > 0) ? (Node*)new (C) MinINode(pre_limit, orig_limit)
                                  : (Node*)new (C) MaxINode(pre_limit, orig_limit);
     register_new_node(pre_limit, pre_ctrl);
--- a/src/share/vm/opto/loopnode.hpp	Sat Sep 26 19:05:33 2020 +0100
+++ b/src/share/vm/opto/loopnode.hpp	Mon Nov 02 02:26:23 2020 +0000
@@ -945,9 +945,9 @@
   // always holds true.  That is, either increase the number of iterations in
   // the pre-loop or the post-loop until the condition holds true in the main
   // loop.  Scale_con, offset and limit are all loop invariant.
-  void add_constraint( int stride_con, int scale_con, Node *offset, Node *low_limit, Node *upper_limit, Node *pre_ctrl, Node **pre_limit, Node **main_limit );
+  void add_constraint(jlong stride_con, jlong scale_con, Node* offset, Node* low_limit, Node* upper_limit, Node* pre_ctrl, Node** pre_limit, Node** main_limit);
   // Helper function for add_constraint().
-  Node* adjust_limit( int stride_con, Node * scale, Node *offset, Node *rc_limit, Node *loop_limit, Node *pre_ctrl );
+  Node* adjust_limit(bool reduce, Node* scale, Node* offset, Node* rc_limit, Node* old_limit, Node* pre_ctrl, bool round);
 
   // Partially peel loop up through last_peel node.
   bool partial_peel( IdealLoopTree *loop, Node_List &old_new );
--- a/src/share/vm/opto/type.cpp	Sat Sep 26 19:05:33 2020 +0100
+++ b/src/share/vm/opto/type.cpp	Mon Nov 02 02:26:23 2020 +0000
@@ -2780,12 +2780,8 @@
 // Type-specific hashing function.
 int TypeOopPtr::hash(void) const {
   return
-    (const_oop() ? const_oop()->hash() : 0) +
-    _klass_is_exact +
-    _instance_id +
-    TypePtr::hash();
     java_add(java_add(const_oop() ? const_oop()->hash() : 0, _klass_is_exact),
-             java_add(_instance_id, TypePtr::hash()));
+             java_add(_instance_id , TypePtr::hash()));
 }
 
 //------------------------------dump2------------------------------------------
@@ -3916,7 +3912,7 @@
 //------------------------------hash-------------------------------------------
 // Type-specific hashing function.
 int TypeKlassPtr::hash(void) const {
-  return klass()->hash() + TypeOopPtr::hash();
+  return java_add(klass()->hash(), TypeOopPtr::hash());
 }
 
 
--- a/src/share/vm/prims/nativeLookup.cpp	Sat Sep 26 19:05:33 2020 +0100
+++ b/src/share/vm/prims/nativeLookup.cpp	Mon Nov 02 02:26:23 2020 +0000
@@ -57,27 +57,118 @@
 #endif
 
 
-static void mangle_name_on(outputStream* st, Symbol* name, int begin, int end) {
+/*
+
+The JNI specification defines the mapping from a Java native method name to
+a C native library implementation function name as follows:
+
+  The mapping produces a native method name by concatenating the following components
+  derived from a `native` method declaration:
+
+  1. the prefix Java_
+  2. given the binary name, in internal form, of the class which declares the native method:
+     the result of escaping the name.
+  3. an underscore ("_")
+  4. the escaped method name
+  5. if the native method declaration is overloaded: two underscores ("__") followed by the
+   escaped parameter descriptor (JVMS 4.3.3) of the method declaration.
+
+  Escaping leaves every alphanumeric ASCII character (A-Za-z0-9) unchanged, and replaces each
+  UTF-16 code unit n the table below with the corresponding escape sequence. If the name to be
+  escaped contains a surrogate pair, then the high-surrogate code unit and the low-surrogate code
+  unit are escaped separately. The result of escaping is a string consisting only of the ASCII
+  characters A-Za-z0-9 and underscore.
+
+  ------------------------------                  ------------------------------------
+  UTF-16 code unit                                Escape sequence
+  ------------------------------                  ------------------------------------
+  Forward slash (/, U+002F)                       _
+  Underscore (_, U+005F)                          _1
+  Semicolon (;, U+003B)                           _2
+  Left square bracket ([, U+005B)                 _3
+  Any UTF-16 code unit \u_WXYZ_ that does not     _0wxyz where w, x, y, and z are the lower-case
+  represent alphanumeric ASCII (A-Za-z0-9),       forms of the hexadecimal digits W, X, Y, and Z.
+  forward slash, underscore, semicolon,           (For example, U+ABCD becomes _0abcd.)
+  or left square bracket
+  ------------------------------                  ------------------------------------
+
+  Note that escape sequences can safely begin _0, _1, etc, because class and method
+  names in Java source code never begin with a number. However, that is not the case in
+  class files that were not generated from Java source code.
+
+  To preserve the 1:1 mapping to a native method name, the VM checks the resulting name as
+  follows. If the process of escaping any precursor string from the native  method declaration
+  (class or method name, or argument type) causes a "0", "1", "2", or "3" character
+  from the precursor string to appear unchanged in the result *either* immediately after an
+  underscore *or* at the beginning of the escaped string (where it will follow an underscore
+  in the fully assembled name), then the escaping process is said to have "failed".
+  In such cases, no native library search is performed, and the attempt to link the native
+  method invocation will throw UnsatisfiedLinkError.
+
+
+For example:
+
+  package/my_class/method
+
+and
+
+  package/my/1class/method
+
+both map to
+
+  Java_package_my_1class_method
+
+To address this potential conflict we need only check if the character after
+/ is a digit 0..3, or if the first character after an injected '_' seperator
+is a digit 0..3. If we encounter an invalid identifier we reset the
+stringStream and return false. Otherwise the stringStream contains the mapped
+name and we return true.
+
+To address legacy compatibility, the UseLegacyJNINameEscaping flag can be set
+which skips the extra checks.
+
+*/
+static bool map_escaped_name_on(stringStream* st, Symbol* name, int begin, int end) {
   char* bytes = (char*)name->bytes() + begin;
   char* end_bytes = (char*)name->bytes() + end;
+  bool check_escape_char = true; // initially true as first character here follows '_'
   while (bytes < end_bytes) {
     jchar c;
     bytes = UTF8::next(bytes, &c);
     if (c <= 0x7f && isalnum(c)) {
+      if (check_escape_char && (c >= '0' && c <= '3') &&
+          !UseLegacyJNINameEscaping) {
+        // This is a non-Java identifier and we won't escape it to
+        // ensure no name collisions with a Java identifier.
+        if (PrintJNIResolving) {
+          ResourceMark rm;
+          tty->print_cr("[Lookup of native method with non-Java identifier rejected: %s]",
+                                  name->as_C_string());
+        }
+        st->reset();  // restore to "" on error
+        return false;
+      }
       st->put((char) c);
+      check_escape_char = false;
     } else {
-           if (c == '_') st->print("_1");
-      else if (c == '/') st->print("_");
+      check_escape_char = false;
+      if (c == '_') st->print("_1");
+      else if (c == '/') {
+        st->print("_");
+        // Following a / we must have non-escape character
+        check_escape_char = true;
+      }
       else if (c == ';') st->print("_2");
       else if (c == '[') st->print("_3");
       else               st->print("_%.5x", c);
     }
   }
+  return true;
 }
 
 
-static void mangle_name_on(outputStream* st, Symbol* name) {
-  mangle_name_on(st, name, 0, name->utf8_length());
+static bool map_escaped_name_on(stringStream* st, Symbol* name) {
+  return map_escaped_name_on(st, name, 0, name->utf8_length());
 }
 
 
@@ -86,10 +177,14 @@
   // Prefix
   st.print("Java_");
   // Klass name
-  mangle_name_on(&st, method->klass_name());
+  if (!map_escaped_name_on(&st, method->klass_name())) {
+    return NULL;
+  }
   st.print("_");
   // Method name
-  mangle_name_on(&st, method->name());
+  if (!map_escaped_name_on(&st, method->name())) {
+    return NULL;
+  }
   return st.as_string();
 }
 
@@ -99,16 +194,20 @@
   // Prefix
   st.print("JavaCritical_");
   // Klass name
-  mangle_name_on(&st, method->klass_name());
+  if (!map_escaped_name_on(&st, method->klass_name())) {
+    return NULL;
+  }
   st.print("_");
   // Method name
-  mangle_name_on(&st, method->name());
+  if (!map_escaped_name_on(&st, method->name())) {
+    return NULL;
+  }
   return st.as_string();
 }
 
 
 char* NativeLookup::long_jni_name(methodHandle method) {
-  // Signature ignore the wrapping parenteses and the trailing return type
+  // Signatures ignore the wrapping parentheses and the trailing return type
   stringStream st;
   Symbol* signature = method->signature();
   st.print("__");
@@ -116,7 +215,10 @@
   int end;
   for (end = 0; end < signature->utf8_length() && signature->byte_at(end) != ')'; end++);
   // skip first '('
-  mangle_name_on(&st, signature, 1, end);
+  if (!map_escaped_name_on(&st, signature, 1, end)) {
+    return NULL;
+  }
+
   return st.as_string();
 }
 
@@ -246,6 +348,11 @@
   in_base_library = false;
   // Compute pure name
   char* pure_name = pure_jni_name(method);
+  if (pure_name == NULL) {
+    // JNI name mapping rejected this method so return
+    // NULL to indicate UnsatisfiedLinkError should be thrown.
+    return NULL;
+  }
 
   // Compute argument size
   int args_size = 1                             // JNIEnv
@@ -259,6 +366,11 @@
 
   // Compute long name
   char* long_name = long_jni_name(method);
+  if (long_name == NULL) {
+    // JNI name mapping rejected this method so return
+    // NULL to indicate UnsatisfiedLinkError should be thrown.
+    return NULL;
+  }
 
   // 2) Try JNI long style
   entry = lookup_style(method, pure_name, long_name, args_size, true,  in_base_library, CHECK_NULL);
@@ -298,6 +410,11 @@
 
   // Compute critical name
   char* critical_name = critical_jni_name(method);
+  if (critical_name == NULL) {
+    // JNI name mapping rejected this method so return
+    // NULL to indicate UnsatisfiedLinkError should be thrown.
+    return NULL;
+  }
 
   // Compute argument size
   int args_size = 1                             // JNIEnv
@@ -311,6 +428,11 @@
 
   // Compute long name
   char* long_name = long_jni_name(method);
+  if (long_name == NULL) {
+    // JNI name mapping rejected this method so return
+    // NULL to indicate UnsatisfiedLinkError should be thrown.
+    return NULL;
+  }
 
   // 2) Try JNI long style
   entry = lookup_critical_style(method, critical_name, long_name, args_size, true);
--- a/src/share/vm/runtime/globals.hpp	Sat Sep 26 19:05:33 2020 +0100
+++ b/src/share/vm/runtime/globals.hpp	Mon Nov 02 02:26:23 2020 +0000
@@ -658,6 +658,9 @@
   product(bool, CriticalJNINatives, true,                                   \
           "check for critical JNI entry points")                            \
                                                                             \
+  product(bool, UseLegacyJNINameEscaping, false,                            \
+          "Use the original JNI name escaping scheme")                      \
+                                                                            \
   notproduct(bool, StressCriticalJNINatives, false,                         \
             "Exercise register saving code in critical natives")            \
                                                                             \
--- a/src/share/vm/utilities/hashtable.cpp	Sat Sep 26 19:05:33 2020 +0100
+++ b/src/share/vm/utilities/hashtable.cpp	Mon Nov 02 02:26:23 2020 +0000
@@ -97,7 +97,7 @@
 template <class T, MEMFLAGS F> unsigned int Hashtable<T, F>::new_hash(Symbol* sym) {
   ResourceMark rm;
   // Use alternate hashing algorithm on this symbol.
-  return AltHashing::murmur3_32(seed(), (const jbyte*)sym->as_C_string(), sym->utf8_length());
+  return AltHashing::halfsiphash_32(seed(), (const uint8_t*)sym->as_C_string(), sym->utf8_length());
 }
 
 template <class T, MEMFLAGS F> unsigned int Hashtable<T, F>::new_hash(oop string) {
@@ -105,7 +105,7 @@
   int length;
   jchar* chars = java_lang_String::as_unicode_string(string, length);
   // Use alternate hashing algorithm on the string
-  return AltHashing::murmur3_32(seed(), chars, length);
+  return AltHashing::halfsiphash_32(seed(), chars, length);
 }
 
 // Create a new table and using alternate hash code, populate the new table
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/compiler/rangechecks/RangeCheckEliminationScaleNotOne.java	Mon Nov 02 02:26:23 2020 +0000
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2018, Red Hat, Inc. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8215265
+ * @summary C2: range check elimination may allow illegal out of bound access
+ *
+ * @run main/othervm -XX:-TieredCompilation -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:-UseLoopPredicate RangeCheckEliminationScaleNotOne
+ *
+ */
+
+import java.util.Arrays;
+
+public class RangeCheckEliminationScaleNotOne {
+    public static void main(String[] args) {
+        {
+            int[] array = new int[199];
+            boolean[] flags = new boolean[100];
+            Arrays.fill(flags, true);
+            flags[0] = false;
+            flags[1] = false;
+            for (int i = 0; i < 20_000; i++) {
+                test1(100, array, 0, flags);
+            }
+            boolean ex = false;
+            try {
+                test1(100, array, -5, flags);
+            } catch (ArrayIndexOutOfBoundsException aie) {
+                ex = true;
+            }
+            if (!ex) {
+                throw new RuntimeException("no AIOOB exception");
+            }
+        }
+
+        {
+            int[] array = new int[199];
+            boolean[] flags = new boolean[100];
+            Arrays.fill(flags, true);
+            flags[0] = false;
+            flags[1] = false;
+            for (int i = 0; i < 20_000; i++) {
+                test2(100, array, 198, flags);
+            }
+            boolean ex = false;
+            try {
+                test2(100, array, 203, flags);
+            } catch (ArrayIndexOutOfBoundsException aie) {
+                ex = true;
+            }
+            if (!ex) {
+                throw new RuntimeException("no AIOOB exception");
+            }
+        }
+    }
+
+    private static int test1(int stop, int[] array, int offset, boolean[] flags) {
+        if (array == null) {}
+        int res = 0;
+        for (int i = 0; i < stop; i++) {
+            if (flags[i]) {
+                res += array[2 * i + offset];
+            }
+        }
+        return res;
+    }
+
+
+    private static int test2(int stop, int[] array, int offset, boolean[] flags) {
+        if (array == null) {}
+        int res = 0;
+        for (int i = 0; i < stop; i++) {
+            if (flags[i]) {
+                res += array[-2 * i + offset];
+            }
+        }
+        return res;
+    }
+}