Skip to content
Snippets Groups Projects
Commit b02471be authored by Philippe Suter's avatar Philippe Suter
Browse files

Proper hashing for records in codegen.

This commit introduces a proper hashCode function for case classes and
tuples used in codegen. It also changes the internal datastructure for
maps and sets from TreeX to HashX.
parent 5c7f1947
No related branches found
No related tags found
No related merge requests found
package leon.codegen.runtime;
// MurmurHash3, reproduced from std. Scala lib.
public final class LeonCodeGenRuntimeHashing {
public static final int seqHash(Object[] elements, int seed) {
// I feel good about this line.
int h = avalanche(seed ^ 0xcafebabe);
int sz = elements.length;
if(sz == 0) return h;
for(int i = 0; i < sz; i++) {
h = mix(h, elements[i].hashCode());
}
return finalizeHash(h, sz);
}
private static final int mix(int hash, int data) {
int h = mixLast(hash, data);
h = Integer.rotateLeft(h, 13);
return h * 5 + 0xe6546b64;
}
private static final int mixLast(int hash, int data) {
int k = data;
k *= 0xcc9e2d51;
k = Integer.rotateLeft(k, 15);
k *= 0x1b873593;
return hash ^ k;
}
private static final int finalizeHash(int hash, int length) {
return avalanche(hash ^ length);
}
private static final int avalanche(int hash) {
int h = hash;
h ^= h >>> 16;
h *= 0x85ebca6b;
h ^= h >>> 13;
h *= 0xc2b2ae35;
h ^= h >>> 16;
return h;
}
}
......@@ -4,22 +4,22 @@ package leon.codegen.runtime;
import java.util.Arrays;
import java.util.Iterator;
import java.util.TreeMap;
import java.util.HashMap;
/** If someone wants to make it an efficient implementation of immutable
* maps, go ahead. */
public final class Map {
private final TreeMap<Object,Object> _underlying;
private final HashMap<Object,Object> _underlying;
protected final TreeMap<Object,Object> underlying() {
protected final HashMap<Object,Object> underlying() {
return _underlying;
}
public Map() {
_underlying = new TreeMap<Object,Object>();
_underlying = new HashMap<Object,Object>();
}
private Map(TreeMap<Object,Object> u) {
private Map(HashMap<Object,Object> u) {
_underlying = u;
}
......@@ -49,7 +49,7 @@ public final class Map {
}
public Map union(Map s) {
TreeMap<Object,Object> n = new TreeMap<Object,Object>(_underlying);
HashMap<Object,Object> n = new HashMap<Object,Object>(_underlying);
n.putAll(s.underlying());
return new Map(n);
}
......
......@@ -4,23 +4,23 @@ package leon.codegen.runtime;
import java.util.Arrays;
import java.util.Iterator;
import java.util.TreeSet;
import java.util.HashSet;
/** If someone wants to make it an efficient implementation of immutable
* sets, go ahead. */
public final class Set {
private final TreeSet<Object> _underlying;
private final HashSet<Object> _underlying;
protected final TreeSet<Object> underlying() {
protected final HashSet<Object> underlying() {
return _underlying;
}
public Set() {
_underlying = new TreeSet<Object>();
_underlying = new HashSet<Object>();
}
public Set(Object[] elements) {
_underlying = new TreeSet<Object>(Arrays.asList(elements));
_underlying = new HashSet<Object>(Arrays.asList(elements));
}
// Uses mutation! Useful at building time.
......@@ -32,7 +32,7 @@ public final class Set {
return _underlying.iterator();
}
private Set(TreeSet<Object> u) {
private Set(HashSet<Object> u) {
_underlying = u;
}
......@@ -54,13 +54,13 @@ public final class Set {
}
public Set union(Set s) {
TreeSet<Object> n = new TreeSet<Object>(_underlying);
HashSet<Object> n = new HashSet<Object>(_underlying);
n.addAll(s.underlying());
return new Set(n);
}
public Set intersect(Set s) {
TreeSet<Object> n = new TreeSet<Object>();
HashSet<Object> n = new HashSet<Object>();
for(Object o : _underlying) {
if(s.underlying().contains(o)) {
n.add(o);
......@@ -70,7 +70,7 @@ public final class Set {
}
public Set minus(Set s) {
TreeSet<Object> n = new TreeSet<Object>(_underlying);
HashSet<Object> n = new HashSet<Object>(_underlying);
for(Object o : s.underlying()) {
n.remove(o);
}
......
......@@ -32,8 +32,18 @@ public final class Tuple {
Tuple other = (Tuple)that;
if(other.getArity() != this.getArity()) return false;
for(int i = 0; i < this.getArity(); i++) {
if(other.get(i) != this.get(i)) return false;
if(!other.get(i).equals(this.get(i))) return false;
}
return true;
}
private int _hash = 0;
@Override
final public int hashCode() {
if(_hash != 0) return _hash;
int seed = (new String("Tuple" + getArity())).hashCode();
int h = LeonCodeGenRuntimeHashing.seqHash(elements, seed);
_hash = h;
return h;
}
}
......@@ -25,6 +25,7 @@ object CodeGeneration {
private val CaseClassClass = "leon/codegen/runtime/CaseClass"
private val ErrorClass = "leon/codegen/runtime/LeonCodeGenRuntimeException"
private val ImpossibleEvaluationClass = "leon/codegen/runtime/LeonCodeGenEvaluationException"
private val HashingClass = "leon/codegen/runtime/LeonCodeGenRuntimeHashing"
def defToJVMName(p : Program, d : Definition) : String = "Leon$CodeGen$" + d.id.uniqueName
......@@ -645,6 +646,8 @@ object CodeGeneration {
// definition of hashcode
locally {
val hashFieldName = "$leon$hashCode"
cf.addField("I", hashFieldName).setFlags((FIELD_ACC_PRIVATE).asInstanceOf[U2])
val hmh = cf.addMethod("I", "hashCode", "")
hmh.setFlags((
METHOD_ACC_PUBLIC |
......@@ -652,8 +655,20 @@ object CodeGeneration {
).asInstanceOf[U2])
val hch = hmh.codeHandler
// TODO FIXME. Look at Scala for inspiration.
hch << Ldc(42) << IRETURN
val wasNotCached = hch.getFreshLabel("wasNotCached")
hch << ALoad(0) << GetField(cName, hashFieldName, "I") << DUP
hch << IfEq(wasNotCached)
hch << IRETURN
hch << Label(wasNotCached) << POP
hch << ALoad(0) << InvokeVirtual(cName, "productElements", "()[Ljava/lang/Object;")
hch << ALoad(0) << InvokeVirtual(cName, "productName", "()Ljava/lang/String;")
hch << InvokeVirtual("java/lang/String", "hashCode", "()I")
hch << InvokeStatic(HashingClass, "seqHash", "([Ljava/lang/Object;I)I") << DUP
hch << ALoad(0) << SWAP << PutField(cName, hashFieldName, "I")
hch << IRETURN
hch.freeze
}
......
......@@ -407,6 +407,28 @@ class EvaluatorsTests extends FunSuite {
}
}
test("Sets and maps of structures") {
val p = """|object Program {
| case class MyPair(x : Int, y : Boolean)
|
| def buildPairCC(x : Int, y : Boolean) : MyPair = MyPair(x,y)
| def mkSingletonCC(p : MyPair) : Set[MyPair] = Set(p)
| def containsCC(s : Set[MyPair], p : MyPair) : Boolean = s.contains(p)
|
| def buildPairT(x : Int, y : Boolean) : (Int,Boolean) = (x,y)
| def mkSingletonT(p : (Int,Boolean)) : Set[(Int,Boolean)] = Set(p)
| def containsT(s : Set[(Int,Boolean)], p : (Int,Boolean)) : Boolean = s.contains(p)
|}""".stripMargin
implicit val progs = parseString(p)
val evaluators = prepareEvaluators
for(e <- evaluators) {
checkComp(e, mkCall("containsCC", mkCall("mkSingletonCC", mkCall("buildPairCC", IL(42), T)), mkCall("buildPairCC", IL(42), T)), T)
checkComp(e, mkCall("containsT", mkCall("mkSingletonT", mkCall("buildPairT", IL(42), T)), mkCall("buildPairT", IL(42), T)), T)
}
}
test("Misc") {
val p = """|object Program {
| import leon.Utils._
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment