Skip to content

Commit 48a6f25

Browse files
committed
[GR-21107] Implement 'ob_digit' for Python integer objects.
PullRequest: graalpython/815
2 parents ec3f169 + 488bb3a commit 48a6f25

File tree

6 files changed

+302
-8
lines changed

6 files changed

+302
-8
lines changed

graalpython/com.oracle.graal.python.cext/src/capi.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -171,6 +171,7 @@ initialize_type(_PyWeakref_CallableProxyType, CallableProxyType, PyWeakReference
171171

172172
POLYGLOT_DECLARE_TYPE(PyThreadState);
173173
POLYGLOT_DECLARE_TYPE(newfunc);
174+
POLYGLOT_DECLARE_TYPE(uint32_t);
174175

175176
static void initialize_globals() {
176177
// register native NULL
@@ -322,6 +323,11 @@ polyglot_typeid get_byte_array_typeid(uint64_t len) {
322323
return polyglot_array_typeid(polyglot_i8_typeid(), len);
323324
}
324325

326+
/** to be used from Java code only; returns the type ID for a uint32_t array */
327+
polyglot_typeid get_uint32_t_array_typeid(uint64_t len) {
328+
return polyglot_array_typeid(polyglot_uint32_t_typeid(), len);
329+
}
330+
325331
/** to be used from Java code only; returns the type ID for a 'PyObject*' array */
326332
polyglot_typeid get_ptr_array_typeid(uint64_t len) {
327333
return polyglot_array_typeid(polyglot_PyObjectPtr_typeid(), len);

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/DynamicObjectNativeWrapper.java

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -601,14 +601,10 @@ Object doObBase(PString o, @SuppressWarnings("unused") String key,
601601
return toSulongNode.execute(o);
602602
}
603603

604-
@Specialization(guards = "eq(OB_SIZE, key)", limit = "getCallSiteInlineCacheMaxDepth()")
604+
@Specialization(guards = "eq(OB_SIZE, key)")
605605
long doObSize(Object object, @SuppressWarnings("unused") String key,
606-
@CachedLibrary("object") PythonObjectLibrary lib) {
607-
try {
608-
return lib.length(object);
609-
} catch (PException e) {
610-
return -1;
611-
}
606+
@Cached ObSizeNode obSizeNode) {
607+
return obSizeNode.execute(object);
612608
}
613609

614610
@Specialization(guards = "eq(MA_USED, key)", limit = "getCallSiteInlineCacheMaxDepth()")
@@ -648,6 +644,21 @@ Object doObItem(PSequence object, @SuppressWarnings("unused") String key) {
648644
return new PySequenceArrayWrapper(object, 4);
649645
}
650646

647+
@Specialization(guards = "eq(OB_DIGIT, key)")
648+
Object doObDigit(int object, @SuppressWarnings("unused") String key) {
649+
return new PyLongDigitsWrapper(object);
650+
}
651+
652+
@Specialization(guards = "eq(OB_DIGIT, key)")
653+
Object doObDigit(long object, @SuppressWarnings("unused") String key) {
654+
return new PyLongDigitsWrapper(object);
655+
}
656+
657+
@Specialization(guards = "eq(OB_DIGIT, key)")
658+
Object doObDigit(PInt object, @SuppressWarnings("unused") String key) {
659+
return new PyLongDigitsWrapper(object);
660+
}
661+
651662
@Specialization(guards = "eq(UNICODE_WSTR, key)")
652663
Object doWstr(PString object, @SuppressWarnings("unused") String key,
653664
@Shared("asWideCharNode") @Cached(value = "createNativeOrder()", uncached = "getUncachedNativeOrder()") UnicodeAsWideCharNode asWideCharNode,
@@ -1519,4 +1530,44 @@ protected Object getNativeType(
15191530
@Cached PGetDynamicTypeNode getDynamicTypeNode) {
15201531
return getDynamicTypeNode.execute(this);
15211532
}
1533+
1534+
/**
1535+
* Depending on the object's type, the size may need to be computed in very different ways. E.g.
1536+
* any PyVarObject usually returns the number of contained elements.
1537+
*/
1538+
@GenerateUncached
1539+
@ImportStatic(PythonOptions.class)
1540+
abstract static class ObSizeNode extends Node {
1541+
1542+
public abstract long execute(Object object);
1543+
1544+
@Specialization
1545+
static long doInteger(@SuppressWarnings("unused") int object) {
1546+
return 1;
1547+
}
1548+
1549+
@Specialization
1550+
static long doLong(@SuppressWarnings("unused") long object) {
1551+
return 2;
1552+
}
1553+
1554+
@Specialization
1555+
static long doPInt(PInt object) {
1556+
return object.bitCount() / 32;
1557+
}
1558+
1559+
@Specialization(limit = "getCallSiteInlineCacheMaxDepth()")
1560+
static long doOther(Object object,
1561+
@CachedLibrary("object") PythonObjectLibrary lib) {
1562+
try {
1563+
return lib.length(object);
1564+
} catch (PException e) {
1565+
return -1;
1566+
}
1567+
}
1568+
1569+
static boolean isFallback(Object object) {
1570+
return !(object instanceof PInt);
1571+
}
1572+
}
15221573
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/NativeCAPISymbols.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ public abstract class NativeCAPISymbols {
9393
public static final String FUN_ALLOCATE_OUTVAR = "allocate_outvar";
9494
public static final String FUN_NATIVE_UNICODE_AS_STRING = "native_unicode_as_string";
9595
public static final String FUN_PY_UNICODE_GET_LENGTH = "PyUnicode_GetLength";
96+
public static final String FUN_GET_UINT32_ARRAY_TYPE_ID = "get_uint32_array_typeid";
9697

9798
@CompilationFinal(dimensions = 1) private static final String[] values;
9899
static {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/NativeMemberNames.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ public final class NativeMemberNames {
142142
public static final String MP_SUBSCRIPT = "mp_subscript";
143143
public static final String MP_ASS_SUBSCRIPT = "mp_ass_subscript";
144144
public static final String OB_FVAL = "ob_fval";
145+
public static final String OB_DIGIT = "ob_digit";
145146
public static final String START = "start";
146147
public static final String STOP = "stop";
147148
public static final String STEP = "step";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
/*
2+
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package com.oracle.graal.python.builtins.objects.cext;
42+
43+
import static com.oracle.graal.python.builtins.objects.cext.NativeCAPISymbols.FUN_GET_UINT32_ARRAY_TYPE_ID;
44+
45+
import com.oracle.graal.python.PythonLanguage;
46+
import com.oracle.graal.python.builtins.objects.cext.CExtNodes.PCallCapiFunction;
47+
import com.oracle.graal.python.builtins.objects.ints.PInt;
48+
import com.oracle.graal.python.runtime.PythonOptions;
49+
import com.oracle.truffle.api.Assumption;
50+
import com.oracle.truffle.api.CompilerAsserts;
51+
import com.oracle.truffle.api.dsl.Cached;
52+
import com.oracle.truffle.api.dsl.Cached.Exclusive;
53+
import com.oracle.truffle.api.dsl.Specialization;
54+
import com.oracle.truffle.api.interop.InteropLibrary;
55+
import com.oracle.truffle.api.interop.InvalidArrayIndexException;
56+
import com.oracle.truffle.api.interop.UnsupportedMessageException;
57+
import com.oracle.truffle.api.library.CachedLibrary;
58+
import com.oracle.truffle.api.library.ExportLibrary;
59+
import com.oracle.truffle.api.library.ExportMessage;
60+
import com.oracle.truffle.llvm.spi.NativeTypeLibrary;
61+
62+
/**
63+
* Emulates {@code ob_digit} of {@code struct _longobject} for Python integers.
64+
*/
65+
@ExportLibrary(InteropLibrary.class)
66+
@ExportLibrary(NativeTypeLibrary.class)
67+
public final class PyLongDigitsWrapper extends PythonNativeWrapper {
68+
69+
public PyLongDigitsWrapper(int delegate) {
70+
super(delegate);
71+
}
72+
73+
public PyLongDigitsWrapper(long delegate) {
74+
super(delegate);
75+
}
76+
77+
public PyLongDigitsWrapper(PInt delegate) {
78+
super(delegate);
79+
}
80+
81+
@Override
82+
public int hashCode() {
83+
CompilerAsserts.neverPartOfCompilation();
84+
final int prime = 31;
85+
int result = 1;
86+
result = prime * result + PythonNativeWrapperLibrary.getUncached().getDelegate(this).hashCode();
87+
return result;
88+
}
89+
90+
@Override
91+
public boolean equals(Object obj) {
92+
if (this == obj) {
93+
return true;
94+
}
95+
if (obj == null) {
96+
return false;
97+
}
98+
if (getClass() != obj.getClass()) {
99+
return false;
100+
}
101+
// n.b.: (tfel) This is hopefully fine here, since if we get to this
102+
// code path, we don't speculate that either of those objects is
103+
// constant anymore, so any caching on them won't happen anyway
104+
PythonNativeWrapperLibrary lib = PythonNativeWrapperLibrary.getUncached();
105+
return lib.getDelegate(this) == lib.getDelegate((PyLongDigitsWrapper) obj);
106+
}
107+
108+
@ExportMessage
109+
final long getArraySize(
110+
@CachedLibrary("this") PythonNativeWrapperLibrary lib) {
111+
Object delegate = lib.getDelegate(this);
112+
if (delegate instanceof Integer) {
113+
return Integer.BYTES / 4;
114+
} else if (delegate instanceof Long) {
115+
return Long.BYTES / 4;
116+
}
117+
assert delegate instanceof PInt;
118+
return ((PInt) delegate).bitCount();
119+
}
120+
121+
@ExportMessage
122+
@SuppressWarnings("static-method")
123+
final boolean hasArrayElements() {
124+
return true;
125+
}
126+
127+
@ExportMessage
128+
final Object readArrayElement(long index,
129+
@CachedLibrary("this") PythonNativeWrapperLibrary lib) throws InvalidArrayIndexException {
130+
Object delegate = lib.getDelegate(this);
131+
if (delegate instanceof Integer) {
132+
if (index == 0) {
133+
return ((Number) delegate).intValue();
134+
}
135+
} else if (delegate instanceof Long) {
136+
if (index >= 0 && index < 2) {
137+
return ((Number) delegate).intValue();
138+
}
139+
} else {
140+
byte[] bytes = ((PInt) delegate).toByteArray();
141+
if (index >= 0 && index < bytes.length / 4) {
142+
// the cast to int is safe since the length check already succeeded
143+
return getUInt32(bytes, (int) index);
144+
}
145+
}
146+
throw InvalidArrayIndexException.create(index);
147+
}
148+
149+
private static long byteAsULong(byte b) {
150+
return ((long) b) & 0xFFL;
151+
}
152+
153+
public static long getUInt32(byte[] bytes, int index) {
154+
return byteAsULong(bytes[index]) | (byteAsULong(bytes[index + 1]) << 8) | (byteAsULong(bytes[index + 2]) << 16) | (byteAsULong(bytes[index + 3]) << 24);
155+
}
156+
157+
static int getCallSiteInlineCacheMaxDepth() {
158+
return PythonOptions.getCallSiteInlineCacheMaxDepth();
159+
}
160+
161+
@ExportMessage
162+
final boolean isArrayElementReadable(long identifier,
163+
@CachedLibrary("this") PythonNativeWrapperLibrary lib) {
164+
// also include the implicit null-terminator
165+
return 0 <= identifier && identifier <= getArraySize(lib);
166+
}
167+
168+
@ExportMessage
169+
public void toNative(
170+
@CachedLibrary("this") PythonNativeWrapperLibrary lib,
171+
@Cached InvalidateNativeObjectsAllManagedNode invalidateNode,
172+
@Cached PCallCapiFunction callToNativeNode) {
173+
invalidateNode.execute();
174+
if (!lib.isNative(this)) {
175+
Object ptr = callToNativeNode.call(NativeCAPISymbols.FUN_PY_TRUFFLE_INT_ARRAY_TO_NATIVE, this, getArraySize(lib));
176+
setNativePointer(ptr);
177+
}
178+
}
179+
180+
@ExportMessage
181+
public boolean isPointer(
182+
@Cached CExtNodes.IsPointerNode pIsPointerNode) {
183+
return pIsPointerNode.execute(this);
184+
}
185+
186+
@ExportMessage
187+
public long asPointer(
188+
@CachedLibrary(limit = "1") InteropLibrary interopLibrary,
189+
@CachedLibrary("this") PythonNativeWrapperLibrary lib) throws UnsupportedMessageException {
190+
Object nativePointer = lib.getNativePointer(this);
191+
if (nativePointer instanceof Long) {
192+
return (long) nativePointer;
193+
}
194+
return interopLibrary.asPointer(nativePointer);
195+
}
196+
197+
@ExportMessage
198+
@SuppressWarnings("static-method")
199+
protected boolean hasNativeType() {
200+
return true;
201+
}
202+
203+
@ExportMessage
204+
abstract static class GetNativeType {
205+
206+
static Object callGetUInt32ArrayTypeIDUncached(PyLongDigitsWrapper digitsWrapper) {
207+
return PCallCapiFunction.getUncached().call(FUN_GET_UINT32_ARRAY_TYPE_ID, 0);
208+
}
209+
210+
@Specialization(assumptions = "singleContextAssumption()")
211+
static Object doByteArray(@SuppressWarnings("unused") PyLongDigitsWrapper object,
212+
@Exclusive @Cached("callGetUInt32ArrayTypeIDUncached(object)") Object nativeType) {
213+
return nativeType;
214+
}
215+
216+
@Specialization(replaces = "doByteArray")
217+
static Object doByteArrayMultiCtx(@SuppressWarnings("unused") PyLongDigitsWrapper object,
218+
@Cached PCallCapiFunction callGetTypeIDNode) {
219+
return callGetTypeIDNode.call(FUN_GET_UINT32_ARRAY_TYPE_ID, 0);
220+
}
221+
222+
protected static Assumption singleContextAssumption() {
223+
return PythonLanguage.getCurrent().singleContextAssumption;
224+
}
225+
}
226+
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,15 @@ private static final byte byteValueExact(BigInteger value) {
368368
return value.byteValueExact();
369369
}
370370

371+
public byte[] toByteArray() {
372+
return toByteArray(value);
373+
}
374+
375+
@TruffleBoundary
376+
private static byte[] toByteArray(BigInteger delegate) {
377+
return delegate.toByteArray();
378+
}
379+
371380
public static boolean isIntRange(long val) {
372381
return val == (int) val;
373382
}

0 commit comments

Comments
 (0)