altHashing.cpp 8.3 KB
Newer Older
1
/*
2
 * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
 * 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.
 *
 */

#include "precompiled.hpp"
#include "classfile/altHashing.hpp"
#include "classfile/symbolTable.hpp"
#include "classfile/systemDictionary.hpp"
#include "oops/markOop.hpp"
#include "runtime/thread.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
// objects.  We don't want to call the synchronizer hash code to install
// this value because it may safepoint.
36
intptr_t object_hash(Klass* k) {
37 38 39 40 41
  intptr_t hc = k->java_mirror()->mark()->hash();
  return hc != markOopDesc::no_hash ? hc : os::random();
}

// Seed value used for each alternative hash calculated.
42
juint AltHashing::compute_seed() {
43 44
  jlong nanos = os::javaTimeNanos();
  jlong now = os::javaTimeMillis();
45 46 47 48 49 50 51 52 53
  int SEED_MATERIAL[8] = {
            (int) object_hash(SystemDictionary::String_klass()),
            (int) object_hash(SystemDictionary::System_klass()),
            (int) os::random(),  // current thread isn't a java thread
            (int) (((julong)nanos) >> 32),
            (int) nanos,
            (int) (((julong)now) >> 32),
            (int) now,
            (int) (os::javaTimeNanos() >> 2)
54 55 56 57 58 59 60
  };

  return murmur3_32(SEED_MATERIAL, 8);
}


// Murmur3 hashing for Symbol
61 62
juint AltHashing::murmur3_32(juint seed, const jbyte* data, int len) {
  juint h1 = seed;
63 64 65 66 67
  int count = len;
  int offset = 0;

  // body
  while (count >= 4) {
68
    juint k1 = (data[offset] & 0x0FF)
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
        | (data[offset + 1] & 0x0FF) << 8
        | (data[offset + 2] & 0x0FF) << 16
        | data[offset + 3] << 24;

    count -= 4;
    offset += 4;

    k1 *= 0xcc9e2d51;
    k1 = Integer_rotateLeft(k1, 15);
    k1 *= 0x1b873593;

    h1 ^= k1;
    h1 = Integer_rotateLeft(h1, 13);
    h1 = h1 * 5 + 0xe6546b64;
  }

  // tail

  if (count > 0) {
88
    juint k1 = 0;
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111

    switch (count) {
      case 3:
        k1 ^= (data[offset + 2] & 0xff) << 16;
      // fall through
      case 2:
        k1 ^= (data[offset + 1] & 0xff) << 8;
      // fall through
      case 1:
        k1 ^= (data[offset] & 0xff);
      // fall through
      default:
        k1 *= 0xcc9e2d51;
        k1 = Integer_rotateLeft(k1, 15);
        k1 *= 0x1b873593;
        h1 ^= k1;
    }
  }

  // finalization
  h1 ^= len;

  // finalization mix force all bits of a hash block to avalanche
112
  h1 ^= h1 >> 16;
113
  h1 *= 0x85ebca6b;
114
  h1 ^= h1 >> 13;
115
  h1 *= 0xc2b2ae35;
116
  h1 ^= h1 >> 16;
117 118 119 120 121

  return h1;
}

// Murmur3 hashing for Strings
122 123
juint AltHashing::murmur3_32(juint seed, const jchar* data, int len) {
  juint h1 = seed;
124 125 126 127 128 129 130 131

  int off = 0;
  int count = len;

  // body
  while (count >= 2) {
    jchar d1 = data[off++] & 0xFFFF;
    jchar d2 = data[off++];
132
    juint k1 = (d1 | d2 << 16);
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147

    count -= 2;

    k1 *= 0xcc9e2d51;
    k1 = Integer_rotateLeft(k1, 15);
    k1 *= 0x1b873593;

    h1 ^= k1;
    h1 = Integer_rotateLeft(h1, 13);
    h1 = h1 * 5 + 0xe6546b64;
  }

  // tail

  if (count > 0) {
148
    juint k1 = (juint)data[off];
149 150 151 152 153 154 155 156 157 158 159

    k1 *= 0xcc9e2d51;
    k1 = Integer_rotateLeft(k1, 15);
    k1 *= 0x1b873593;
    h1 ^= k1;
  }

  // finalization
  h1 ^= len * 2; // (Character.SIZE / Byte.SIZE);

  // finalization mix force all bits of a hash block to avalanche
160
  h1 ^= h1 >> 16;
161
  h1 *= 0x85ebca6b;
162
  h1 ^= h1 >> 13;
163
  h1 *= 0xc2b2ae35;
164
  h1 ^= h1 >> 16;
165 166 167 168 169

  return h1;
}

// Hash used for the seed.
170 171
juint AltHashing::murmur3_32(juint seed, const int* data, int len) {
  juint h1 = seed;
172 173 174 175 176 177

  int off = 0;
  int end = len;

  // body
  while (off < end) {
178
    juint k1 = (juint)data[off++];
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195

    k1 *= 0xcc9e2d51;
    k1 = Integer_rotateLeft(k1, 15);
    k1 *= 0x1b873593;

    h1 ^= k1;
    h1 = Integer_rotateLeft(h1, 13);
    h1 = h1 * 5 + 0xe6546b64;
  }

  // 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
196
  h1 ^= h1 >> 16;
197
  h1 *= 0x85ebca6b;
198
  h1 ^= h1 >> 13;
199
  h1 *= 0xc2b2ae35;
200
  h1 ^= h1 >> 16;
201 202 203 204

  return h1;
}

205
juint AltHashing::murmur3_32(const int* data, int len) {
206 207 208 209 210
  return murmur3_32(0, data, len);
}

#ifndef PRODUCT
// Overloaded versions for internal test.
211
juint AltHashing::murmur3_32(const jbyte* data, int len) {
212 213 214
  return murmur3_32(0, data, len);
}

215
juint AltHashing::murmur3_32(const jchar* data, int len) {
216 217 218 219 220 221 222 223 224 225 226
  return murmur3_32(0, data, len);
}

// 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};
227
static const jint ONE_INT[] = { (jint) 0x83828180};
228 229 230 231 232 233 234 235 236 237
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};

238
static const jint TWO_INT[] = { (jint) 0x83828180, (jint) 0x87868584};
239 240 241 242 243 244

static const juint MURMUR3_32_X86_CHECK_VALUE = 0xB0F57EE3;

void AltHashing::testMurmur3_32_ByteArray() {
  // printf("testMurmur3_32_ByteArray\n");

245 246
  jbyte vector[256];
  jbyte hashes[4 * 256];
247 248 249 250 251 252 253

  for (int i = 0; i < 256; i++) {
    vector[i] = (jbyte) i;
  }

  // Hash subranges {}, {0}, {0,1}, {0,1,2}, ..., {0,...,255}
  for (int i = 0; i < 256; i++) {
254
    juint hash = murmur3_32(256 - i, vector, i);
255
    hashes[i * 4] = (jbyte) hash;
256 257 258
    hashes[i * 4 + 1] = (jbyte)(hash >> 8);
    hashes[i * 4 + 2] = (jbyte)(hash >> 16);
    hashes[i * 4 + 3] = (jbyte)(hash >> 24);
259 260 261 262 263 264 265 266 267 268 269 270 271
  }

  // 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));
}

void AltHashing::testEquivalentHashes() {
272
  juint jbytes, jchars, ints;
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304

  // 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));

  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));

  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));
}

// Returns true if the alternate hashcode is correct
void AltHashing::test_alt_hash() {
  testMurmur3_32_ByteArray();
  testEquivalentHashes();
}
#endif // PRODUCT