Skip to content

Commit 14a450f

Browse files
committed
Introduce PythonObjectSlowPathFactory and use it in JNI upcalls
1 parent 02e25aa commit 14a450f

File tree

2 files changed

+117
-16
lines changed

2 files changed

+117
-16
lines changed

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

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,6 @@
165165
import com.oracle.graal.python.builtins.objects.type.PythonClass;
166166
import com.oracle.graal.python.builtins.objects.type.SpecialMethodSlot;
167167
import com.oracle.graal.python.builtins.objects.type.TypeNodes.IsTypeNode;
168-
import com.oracle.graal.python.builtins.objects.type.TypeNodesFactory.GetInstanceShapeNodeGen;
169168
import com.oracle.graal.python.lib.CanBeDoubleNodeGen;
170169
import com.oracle.graal.python.lib.PyFloatAsDoubleNodeGen;
171170
import com.oracle.graal.python.lib.PyIndexCheckNodeGen;
@@ -193,6 +192,7 @@
193192
import com.oracle.graal.python.runtime.exception.PException;
194193
import com.oracle.graal.python.runtime.exception.PythonThreadKillException;
195194
import com.oracle.graal.python.runtime.object.PythonObjectFactory;
195+
import com.oracle.graal.python.runtime.object.PythonObjectSlowPathFactory;
196196
import com.oracle.graal.python.runtime.sequence.PSequence;
197197
import com.oracle.graal.python.runtime.sequence.storage.DoubleSequenceStorage;
198198
import com.oracle.graal.python.runtime.sequence.storage.IntSequenceStorage;
@@ -761,6 +761,8 @@ public static GraalHPyNativeSymbol getGetterFunctionName(LLVMType llvmType) {
761761
@CompilationFinal private RootCallTarget referenceCleanerCallTarget;
762762
private Thread hpyReferenceCleanerThread;
763763

764+
private PythonObjectSlowPathFactory slowPathFactory;
765+
764766
public GraalHPyContext(PythonContext context, Object hpyLibrary) {
765767
super(context, hpyLibrary, GraalHPyConversionNodeSupplier.HANDLE);
766768
this.hpyContextMembers = createMembers(context, getName());
@@ -1284,6 +1286,17 @@ void increment() {
12841286
}
12851287
}
12861288

1289+
/**
1290+
* Returns a Python object factory that should only be used on the slow path. The factory object
1291+
* is initialized lazily.
1292+
*/
1293+
private PythonObjectSlowPathFactory factory() {
1294+
if (slowPathFactory == null) {
1295+
slowPathFactory = new PythonObjectSlowPathFactory(getContext().getAllocationReporter());
1296+
}
1297+
return slowPathFactory;
1298+
}
1299+
12871300
@SuppressWarnings("static-method")
12881301
public final long ctxFloatFromDouble(double value) {
12891302
Counter.UpcallFloatFromDouble.increment();
@@ -1353,14 +1366,17 @@ public final long ctxNew(long typeHandle, long dataOutVar) {
13531366
long basicSize = clazz.basicSize;
13541367
if (basicSize == -1) {
13551368
// create the managed Python object
1356-
pythonObject = new PythonObject(clazz, clazz.getInstanceShape());
1369+
pythonObject = factory().createPythonObject(clazz, clazz.getInstanceShape());
13571370
} else {
1371+
/*
1372+
* Since this is a JNI upcall method, we know that (1) we are not running in some
1373+
* managed mode, and (2) the data will be used in real native code. Hence, we can
1374+
* immediately allocate native memory via Unsafe.
1375+
*/
13581376
long dataPtr = unsafe.allocateMemory(basicSize);
13591377
unsafe.setMemory(dataPtr, basicSize, (byte) 0);
13601378
unsafe.putLong(dataOutVar, dataPtr);
1361-
// create the managed Python object
1362-
pythonObject = new PythonHPyObject(clazz, clazz.getInstanceShape(), dataPtr);
1363-
// we fully control this attribute; if it is there, it's always a long
1379+
pythonObject = factory().createPythonHPyObject(clazz, dataPtr);
13641380
Object destroyFunc = clazz.hpyDestroyFunc;
13651381
createHandleReference(pythonObject, dataPtr, destroyFunc != PNone.NO_VALUE ? destroyFunc : null);
13661382
}
@@ -1370,7 +1386,7 @@ public final long ctxNew(long typeHandle, long dataOutVar) {
13701386
return HPyRaiseNodeGen.getUncached().raiseIntWithoutFrame(this, 0, PythonBuiltinClassType.TypeError, "HPy_New arg 1 must be a type");
13711387
}
13721388
// TODO(fa): this should actually call __new__
1373-
pythonObject = new PythonObject(type, GetInstanceShapeNodeGen.getUncached().execute(type));
1389+
pythonObject = factory().createPythonObject(type);
13741390
}
13751391
return GraalHPyBoxing.boxHandle(createHandle(pythonObject).getId(this, ConditionProfile.getUncached()));
13761392
}
@@ -1389,10 +1405,9 @@ public final long ctxTypeGenericNew(long typeHandle) {
13891405
// allocate native space
13901406
long dataPtr = unsafe.allocateMemory(basicSize);
13911407
unsafe.setMemory(dataPtr, basicSize, (byte) 0);
1392-
pythonObject = new PythonHPyObject(clazz, clazz.getInstanceShape(), dataPtr);
1408+
pythonObject = factory().createPythonHPyObject(clazz, dataPtr);
13931409
} else {
1394-
// create the managed Python object
1395-
pythonObject = new PythonObject(clazz, clazz.getInstanceShape());
1410+
pythonObject = factory().createPythonObject(clazz);
13961411
}
13971412
return GraalHPyBoxing.boxHandle(createHandle(pythonObject).getId(this, ConditionProfile.getUncached()));
13981413
}
@@ -1416,14 +1431,14 @@ public final long ctxDup(long handle) {
14161431
}
14171432
}
14181433

1419-
public final long ctxGetItemi(long handle, long lidx) {
1434+
public final long ctxGetItemi(long hCollection, long lidx) {
14201435
Counter.UpcallGetItemI.increment();
14211436
try {
1422-
// If handle 'hSequence' is a boxed int or double, the object is not subscriptable.
1423-
if (!GraalHPyBoxing.isBoxedHandle(handle)) {
1437+
// If handle 'hCollection' is a boxed int or double, the object is not subscriptable.
1438+
if (!GraalHPyBoxing.isBoxedHandle(hCollection)) {
14241439
throw PRaiseNode.raiseUncached(null, PythonBuiltinClassType.TypeError, ErrorMessages.OBJ_NOT_SUBSCRIPTABLE, 0);
14251440
}
1426-
Object receiver = getObjectForHPyHandle(GraalHPyBoxing.unboxHandle(handle)).getDelegate();
1441+
Object receiver = getObjectForHPyHandle(GraalHPyBoxing.unboxHandle(hCollection)).getDelegate();
14271442
Object clazz = GetClassNode.getUncached().execute(receiver);
14281443
if (clazz == PythonBuiltinClassType.PList || clazz == PythonBuiltinClassType.PTuple) {
14291444
if (!PInt.isIntRange(lidx)) {
@@ -1577,7 +1592,7 @@ public final int ctxNumberCheck(long handle) {
15771592
return 1;
15781593
}
15791594
Object receiverType = GetClassNode.getUncached().execute(receiver);
1580-
return com.oracle.graal.python.builtins.objects.ints.PInt.intValue(LookupCallableSlotInMRONode.getUncached(SpecialMethodSlot.Int).execute(receiverType) != PNone.NO_VALUE);
1595+
return PInt.intValue(LookupCallableSlotInMRONode.getUncached(SpecialMethodSlot.Int).execute(receiverType) != PNone.NO_VALUE);
15811596
} catch (PException e) {
15821597
HPyTransformExceptionToNativeNodeGen.getUncached().execute(this, e);
15831598
return 0;
@@ -1607,10 +1622,9 @@ public final long ctxLength(long handle) {
16071622
public final int ctxListCheck(long handle) {
16081623
Counter.UpcallListCheck.increment();
16091624
if (GraalHPyBoxing.isBoxedHandle(handle)) {
1610-
16111625
Object obj = getObjectForHPyHandle(GraalHPyBoxing.unboxHandle(handle)).getDelegate();
16121626
Object clazz = GetClassNode.getUncached().execute(obj);
1613-
return com.oracle.graal.python.builtins.objects.ints.PInt.intValue(clazz == PythonBuiltinClassType.PList || IsSubtypeNodeGen.getUncached().execute(clazz, PythonBuiltinClassType.PList));
1627+
return PInt.intValue(clazz == PythonBuiltinClassType.PList || IsSubtypeNodeGen.getUncached().execute(clazz, PythonBuiltinClassType.PList));
16141628
} else {
16151629
return 0;
16161630
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright (c) 2021, 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.runtime.object;
42+
43+
import java.util.Objects;
44+
45+
import com.oracle.graal.python.builtins.objects.type.TypeNodesFactory.GetInstanceShapeNodeGen;
46+
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
47+
import com.oracle.truffle.api.instrumentation.AllocationReporter;
48+
import com.oracle.truffle.api.nodes.NodeCost;
49+
import com.oracle.truffle.api.object.Shape;
50+
51+
/**
52+
* A subclass of {@link PythonObjectFactory} which is basically an uncached version of it but
53+
* directly stores a reference to the {@link AllocationReporter} instead of doing a context lookup
54+
* and getting it from the context. This class is meant to be used on slow path where the context is
55+
* explicitly available.
56+
*/
57+
public final class PythonObjectSlowPathFactory extends PythonObjectFactory {
58+
59+
private final AllocationReporter reporter;
60+
61+
public PythonObjectSlowPathFactory(AllocationReporter reporter) {
62+
this.reporter = Objects.requireNonNull(reporter);
63+
}
64+
65+
@TruffleBoundary
66+
@Override
67+
protected AllocationReporter executeTrace(Object arg0Value, long arg1Value) {
68+
return PythonObjectFactory.doTrace(arg0Value, arg1Value, reporter);
69+
}
70+
71+
@TruffleBoundary
72+
@Override
73+
protected Shape executeGetShape(Object arg0Value, boolean arg1Value) {
74+
return PythonObjectFactory.getShape(arg0Value, arg1Value, (GetInstanceShapeNodeGen.getUncached()));
75+
}
76+
77+
@Override
78+
public NodeCost getCost() {
79+
return NodeCost.MEGAMORPHIC;
80+
}
81+
82+
@Override
83+
public boolean isAdoptable() {
84+
return false;
85+
}
86+
87+
}

0 commit comments

Comments
 (0)