Skip to content

Commit 9158f2a

Browse files
committed
[GR-24023] Hash of numerical types made consistent with CPython
PullRequest: graalpython/1182
2 parents d4ebd10 + d2ee60a commit 9158f2a

File tree

12 files changed

+81
-63
lines changed

12 files changed

+81
-63
lines changed

graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_fractions.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
*graalpython.lib-python.3.test.test_fractions.FractionTest.testFromDecimal
1212
*graalpython.lib-python.3.test.test_fractions.FractionTest.testFromFloat
1313
*graalpython.lib-python.3.test.test_fractions.FractionTest.testFromString
14+
*graalpython.lib-python.3.test.test_fractions.FractionTest.testHash
1415
*graalpython.lib-python.3.test.test_fractions.FractionTest.testImmutable
1516
*graalpython.lib-python.3.test.test_fractions.FractionTest.testInit
1617
*graalpython.lib-python.3.test.test_fractions.FractionTest.testInitFromDecimal

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PythonCextBuiltins.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,6 @@
173173
import com.oracle.graal.python.builtins.objects.common.PHashingCollection;
174174
import com.oracle.graal.python.builtins.objects.common.SequenceNodes;
175175
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes;
176-
import com.oracle.graal.python.builtins.objects.complex.PComplex;
177176
import com.oracle.graal.python.builtins.objects.dict.PDict;
178177
import com.oracle.graal.python.builtins.objects.exception.PBaseException;
179178
import com.oracle.graal.python.builtins.objects.frame.PFrame;
@@ -1466,7 +1465,7 @@ Object doUnicode(VirtualFrame frame, Object o, Object errorMarker) {
14661465
abstract static class PyHashImagNode extends PythonBuiltinNode {
14671466
@Specialization
14681467
long getHash() {
1469-
return PComplex.IMAG_MULTIPLIER;
1468+
return SysModuleBuiltins.HASH_IMAG;
14701469
}
14711470
}
14721471

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,12 @@ public class SysModuleBuiltins extends PythonBuiltins {
109109
public static final String PLATFORM_WIN32 = "win32";
110110
public static final PNone FRAMEWORK = PNone.NONE;
111111
public static final int MAXSIZE = Integer.MAX_VALUE;
112+
public static final long HASH_MULTIPLIER = 1000003L;
113+
public static final int HASH_BITS = 61;
114+
public static final long HASH_MODULUS = (1L << HASH_BITS) - 1;
115+
public static final long HASH_INF = 314159;
116+
public static final long HASH_NAN = 0;
117+
public static final long HASH_IMAG = HASH_MULTIPLIER;
112118

113119
static {
114120
String compile_time;
@@ -155,6 +161,17 @@ public void initialize(PythonCore core) {
155161
2, // FLT_RADIX
156162
1 // FLT_ROUNDS
157163
}));
164+
builtinConstants.put("hash_info", core.factory().createTuple(new Object[]{
165+
"java", // algorithm
166+
0, // cutoff
167+
64, // hash_bits
168+
HASH_IMAG, // imag
169+
HASH_INF, // inf
170+
HASH_MODULUS, // modulus
171+
HASH_NAN, // nan
172+
0, // seed_bits
173+
64, // width
174+
}));
158175
builtinConstants.put("maxunicode", IntegerFormatter.LIMIT_UNICODE.intValue() - 1);
159176

160177
String os = getPythonOSName();

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/complex/ComplexBuiltins.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
import com.oracle.graal.python.builtins.CoreFunctions;
8282
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
8383
import com.oracle.graal.python.builtins.PythonBuiltins;
84+
import com.oracle.graal.python.builtins.modules.SysModuleBuiltins;
8485
import com.oracle.graal.python.builtins.objects.PNone;
8586
import com.oracle.graal.python.builtins.objects.PNotImplemented;
8687
import com.oracle.graal.python.builtins.objects.common.FormatNodeBase;
@@ -830,7 +831,7 @@ long hash(PComplex self) {
830831
// just like CPython
831832
long realHash = PythonObjectLibrary.hash(self.getReal());
832833
long imagHash = PythonObjectLibrary.hash(self.getImag());
833-
return realHash + PComplex.IMAG_MULTIPLIER * imagHash;
834+
return realHash + SysModuleBuiltins.HASH_IMAG * imagHash;
834835
}
835836
}
836837

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/complex/PComplex.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@
3131
import com.oracle.truffle.api.object.Shape;
3232

3333
public final class PComplex extends PythonBuiltinObject {
34-
/* Prime multiplier used in string and various other hashes in CPython. */
35-
public static final int IMAG_MULTIPLIER = 1000003; /* 0xf4243 */
3634

3735
private final double real;
3836
private final double imag;

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/floats/FloatBuiltins.java

Lines changed: 3 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -663,49 +663,9 @@ protected static boolean accepts(Object obj) {
663663
@GenerateNodeFactory
664664
@TypeSystemReference(PythonArithmeticTypes.class)
665665
abstract static class HashNode extends PythonUnaryBuiltinNode {
666-
protected boolean noDecimals(float num) {
667-
return num % 1 == 0;
668-
}
669-
670-
protected boolean noDecimals(double num) {
671-
return num % 1 == 0;
672-
}
673-
674-
protected boolean noDecimals(PFloat num) {
675-
return num.getValue() % 1 == 0;
676-
}
677-
678-
@Specialization(guards = {"noDecimals(self)"})
679-
long hashFloatNoDecimals(float self) {
680-
return (long) self;
681-
}
682-
683-
@Specialization(guards = {"!noDecimals(self)"})
684-
@TruffleBoundary
685-
long hashFloatWithDecimals(float self) {
686-
return Float.valueOf(self).hashCode();
687-
}
688-
689-
@Specialization(guards = {"noDecimals(self)"})
690-
long hashDoubleNoDecimals(double self) {
691-
return (long) self;
692-
}
693-
694-
@Specialization(guards = {"!noDecimals(self)"})
695-
@TruffleBoundary
696-
long hashDoubleWithDecimals(double self) {
697-
return Double.valueOf(self).hashCode();
698-
}
699-
700-
@Specialization(guards = {"noDecimals(self)"})
701-
long hashPFloatNoDecimals(PFloat self) {
702-
return (long) self.getValue();
703-
}
704-
705-
@Specialization(guards = {"!noDecimals(self)"})
706-
@TruffleBoundary
707-
long hashPFloatWithDecimals(PFloat self) {
708-
return Double.valueOf(self.getValue()).hashCode();
666+
@Specialization
667+
long hashDouble(double self) {
668+
return PythonObjectLibrary.hash(self);
709669
}
710670
}
711671

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ints/IntBuiltins.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2681,25 +2681,26 @@ private static void validateIntegerSpec(PythonCore core, Spec spec) {
26812681
abstract static class HashNode extends PythonUnaryBuiltinNode {
26822682

26832683
@Specialization
2684-
int hash(int self) {
2685-
return self;
2684+
long hash(int self) {
2685+
return PythonObjectLibrary.hash(self);
26862686
}
26872687

26882688
@Specialization
26892689
long hash(long self) {
2690-
return self;
2690+
return PythonObjectLibrary.hash(self);
26912691
}
26922692

26932693
@Specialization
26942694
long hash(PInt self) {
2695-
return self.longValue();
2695+
return self.hash();
26962696
}
26972697

26982698
@Specialization
26992699
@TruffleBoundary
27002700
long hash(PythonNativeVoidPtr self) {
27012701
return self.object.hashCode();
27022702
}
2703+
27032704
}
27042705

27052706
@Builtin(name = "bit_length", minNumOfPositionalArgs = 1)

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ints/PInt.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
import com.oracle.graal.python.PythonLanguage;
3333
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
34+
import com.oracle.graal.python.builtins.modules.SysModuleBuiltins;
3435
import com.oracle.graal.python.builtins.objects.cext.PythonNativeWrapperLibrary;
3536
import com.oracle.graal.python.builtins.objects.function.PArguments.ThreadState;
3637
import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject;
@@ -538,4 +539,15 @@ public BigInteger subtract(PInt other) {
538539
public BigInteger add(PInt other) {
539540
return add(other.value);
540541
}
542+
543+
@ExportMessage
544+
public long hash() {
545+
return hashBigInteger(value);
546+
}
547+
548+
@TruffleBoundary
549+
public static long hashBigInteger(BigInteger i) {
550+
long h = i.remainder(BigInteger.valueOf(SysModuleBuiltins.HASH_MODULUS)).longValue();
551+
return h == -1 ? -2 : h;
552+
}
541553
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/DefaultPythonDoubleExports.java

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343
import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError;
4444

4545
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
46+
import com.oracle.graal.python.builtins.modules.MathModuleBuiltins;
47+
import com.oracle.graal.python.builtins.modules.SysModuleBuiltins;
4648
import com.oracle.graal.python.builtins.objects.PythonAbstractObject;
4749
import com.oracle.graal.python.builtins.objects.floats.PFloat;
4850
import com.oracle.graal.python.builtins.objects.function.PArguments;
@@ -81,13 +83,40 @@ static long hash(Double number) {
8183
return hash(number.doubleValue());
8284
}
8385

86+
// Adapted from CPython _Py_HashDouble
8487
@Ignore
8588
static long hash(double number) {
86-
if (number % 1 == 0) {
87-
return (long) number;
88-
} else {
89-
return Double.doubleToLongBits(number);
89+
if (!Double.isFinite(number)) {
90+
if (Double.isInfinite(number)) {
91+
return number > 0 ? SysModuleBuiltins.HASH_INF : -SysModuleBuiltins.HASH_INF;
92+
}
93+
return SysModuleBuiltins.HASH_NAN;
94+
}
95+
96+
double[] frexpRes = MathModuleBuiltins.FrexpNode.frexp(number);
97+
double m = frexpRes[0];
98+
int e = (int) frexpRes[1];
99+
int sign = 1;
100+
if (m < 0) {
101+
sign = -1;
102+
m = -m;
103+
}
104+
long x = 0;
105+
while (m != 0.0) {
106+
x = ((x << 28) & SysModuleBuiltins.HASH_MODULUS) | x >> (SysModuleBuiltins.HASH_BITS - 28);
107+
m *= 268435456.0; /* 2**28 */
108+
e -= 28;
109+
long y = (long) m; /* pull out integer part */
110+
m -= y;
111+
x += y;
112+
if (x >= SysModuleBuiltins.HASH_MODULUS) {
113+
x -= SysModuleBuiltins.HASH_MODULUS;
114+
}
90115
}
116+
e = e >= 0 ? e % SysModuleBuiltins.HASH_BITS : SysModuleBuiltins.HASH_BITS - 1 - ((-1 - e) % SysModuleBuiltins.HASH_BITS);
117+
x = ((x << e) & SysModuleBuiltins.HASH_MODULUS) | x >> (SysModuleBuiltins.HASH_BITS - e);
118+
x = x * sign;
119+
return x == -1 ? -2 : x;
91120
}
92121

93122
@ExportMessage

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/DefaultPythonIntegerExports.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,12 @@ static Object getLazyPythonClass(@SuppressWarnings("unused") Integer value) {
9191

9292
@ExportMessage
9393
static long hash(Integer value) {
94-
return value;
94+
return hash(value.intValue());
9595
}
9696

9797
@Ignore
9898
static long hash(int value) {
99-
return value;
99+
return value == -1 ? -2 : value;
100100
}
101101

102102
@ExportMessage

0 commit comments

Comments
 (0)