1 /* 2 * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 25 package org.graalvm.compiler.lir.hashing; 26 27 import java.util.ArrayList; 28 import java.util.Collections; 29 import java.util.List; 30 import java.util.function.BiFunction; 31 import java.util.function.Function; 32 33 import org.graalvm.compiler.lir.gen.ArithmeticLIRGenerator; 34 35 import jdk.vm.ci.meta.JavaConstant; 36 import jdk.vm.ci.meta.Value; 37 38 /** 39 * This class provides a set of cheap imperfect hash functions based on the paper "Improving Switch 40 * Statement Performance with Hashing Optimized at Compile Time". 41 * (http://programming.sirrida.de/hashsuper.pdf) 42 */ 43 public abstract class HashFunction { 44 45 /** 46 * Applies the hash function. 47 * 48 * @param value the value to be hashed 49 * @param min {@code value} is guaranteed to be greater or equal to this minimum 50 * @return the hash value within int range 51 */ 52 public abstract int apply(long value, long min); 53 54 /** 55 * Generates LIR that implements the hash function in terms of value and min. 56 * 57 * @param value the value to be hashed 58 * @param min the lowest key 59 * @param gen the lir generator 60 * @return new lir value with the hash function applied 61 */ 62 public abstract Value gen(Value value, Value min, ArithmeticLIRGenerator gen); 63 64 /** 65 * Returns an estimate of number of CPU cycles necessary to apply the hash function. 66 */ 67 public abstract int effort(); 68 69 /** 70 * @return a list of all available hash functions 71 */ 72 public static final List<HashFunction> instances() { 73 return Collections.unmodifiableList(instances); 74 } 75 76 private static List<HashFunction> instances = new ArrayList<>(); 77 78 private static int[] mersennePrimes = {3, 7, 31, 127, 8191, 131071, 524287, 2147483647}; 79 80 static { 81 //@formatter:off 82 83 add("val", 0, 84 (val, min) -> val, 85 gen -> (val, min) -> val); 86 87 add("val - min", 1, 88 (val, min) -> val - min, 89 gen -> (val, min) -> gen.emitSub(val, min, false)); 90 91 add("val >> min", 1, 92 (val, min) -> val >> min, 93 gen -> (val, min) -> gen.emitShr(val, min)); 94 95 add("val >> (val & min)", 2, 96 (val, min) -> val >> (val & min), 97 gen -> (val, min) -> gen.emitShr(val, gen.emitAnd(val, min))); 98 99 add("(val >> min) ^ val", 2, 100 (val, min) -> (val >> min) ^ val, 101 gen -> (val, min) -> gen.emitXor(gen.emitShr(val, min), val)); 102 103 add("(val >> min) * val", 3, 104 (val, min) -> (val >> min) * val, 105 gen -> (val, min) -> gen.emitMul(gen.emitShr(val, min), val, false)); 106 107 addWithPrimes("(val * prime) >> min", 3, 108 prime -> (val, min) -> (val * prime) >> min, 109 (gen, prime) -> (val, min) -> gen.emitShr(gen.emitMul(val, prime, false), min)); 110 111 addWithPrimes("rotateRight(val, prime)", 3, 112 prime -> (val, min) -> Long.rotateRight(val, prime), 113 (gen, prime) -> (val, min) -> gen.emitRor(val, prime)); 114 115 addWithPrimes("rotateRight(val, prime) + val", 4, 116 prime -> (val, min) -> Long.rotateRight(val, prime) + val, 117 (gen, prime) -> (val, min) -> gen.emitAdd(gen.emitRor(val, prime), val, false)); 118 119 addWithPrimes("rotateRight(val, prime) ^ val", 4, 120 prime -> (val, min) -> Long.rotateRight(val, prime) ^ val, 121 (gen, prime) -> (val, min) -> gen.emitXor(gen.emitRor(val, prime), val)); 122 //@formatter:on 123 } 124 125 private static void add(String toString, int effort, BiFunction<Long, Long, Long> f, Function<ArithmeticLIRGenerator, BiFunction<Value, Value, Value>> gen) { 126 instances.add(new HashFunction() { 127 128 @Override 129 public int apply(long value, long min) { 130 return f.apply(value, min).intValue(); 131 } 132 133 @Override 134 public int effort() { 135 return effort; 136 } 137 138 @Override 139 public String toString() { 140 return toString; 141 } 142 143 @Override 144 public Value gen(Value val, Value min, ArithmeticLIRGenerator t) { 145 return gen.apply(t).apply(t.emitNarrow(val, 32), t.emitNarrow(min, 32)); 146 } 147 }); 148 } 149 150 private static void addWithPrimes(String toString, int effort, Function<Integer, BiFunction<Long, Long, Long>> f, 151 BiFunction<ArithmeticLIRGenerator, Value, BiFunction<Value, Value, Value>> gen) { 152 for (int p : mersennePrimes) { 153 add(toString, effort, f.apply(p), g -> gen.apply(g, g.getLIRGen().emitJavaConstant(JavaConstant.forInt(p)))); 154 } 155 } 156 }