Skip to content

Commit a4ff2b9

Browse files
committed
HPy: introduce 'immutable' handles
Some well known constants, such as None, always get the same handle number. This will be used in native fast-paths.
1 parent 4f2fc30 commit a4ff2b9

File tree

4 files changed

+120
-36
lines changed

4 files changed

+120
-36
lines changed

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242

4343
public class GraalHPyBoxing {
4444

45-
// see the corresponding implementation in hpy_native.c
45+
// see the corresponding implementation in hpy_jni.c
4646

4747
/*-
4848
* This NaN boxing mechanism puts all non-double values into the range
@@ -67,6 +67,10 @@ public class GraalHPyBoxing {
6767
private static final long NAN_BOXING_INT_MASK = 0x00000000FFFFFFFFL;
6868
private static final long NAN_BOXING_MAX_HANDLE = Integer.MAX_VALUE;
6969

70+
// First N constants in the HPyContext are guaranteed to always get the same handle assigned.
71+
// Note that 0 is HPy_NULL, so in this case we are counting from 1.
72+
public static final int SINGLETON_HANDLE_MAX = 3;
73+
7074
public static boolean isBoxedDouble(long value) {
7175
return Long.compareUnsigned(value, NAN_BOXING_BASE) >= 0;
7276
}

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

Lines changed: 73 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
import com.oracle.graal.python.lib.PyObjectGetItem;
8989
import com.oracle.graal.python.lib.PyObjectSetItem;
9090
import com.oracle.graal.python.runtime.GilNode.UncachedAcquire;
91+
import com.oracle.truffle.api.dsl.Fallback;
9192
import org.graalvm.nativeimage.ImageInfo;
9293

9394
import com.oracle.graal.python.PythonLanguage;
@@ -296,7 +297,6 @@
296297
import com.oracle.truffle.api.nodes.Node;
297298
import com.oracle.truffle.api.object.DynamicObjectLibrary;
298299
import com.oracle.truffle.api.object.Shape;
299-
import com.oracle.truffle.api.profiles.ConditionProfile;
300300
import com.oracle.truffle.api.source.Source;
301301
import com.oracle.truffle.api.source.Source.SourceBuilder;
302302
import com.oracle.truffle.llvm.spi.NativeTypeLibrary;
@@ -902,7 +902,7 @@ public static GraalHPyNativeSymbol getGetterFunctionName(LLVMType llvmType) {
902902

903903
private static final int IMMUTABLE_HANDLE_COUNT = 256;
904904

905-
private Object[] hpyHandleTable = new Object[]{GraalHPyHandle.NULL_HANDLE_DELEGATE};
905+
private Object[] hpyHandleTable;
906906
private int nextHandle = 1;
907907

908908
private GraalHPyHandle[] hpyGlobalsTable = new GraalHPyHandle[]{GraalHPyHandle.NULL_HANDLE};
@@ -933,6 +933,14 @@ public static GraalHPyNativeSymbol getGetterFunctionName(LLVMType llvmType) {
933933

934934
@CompilationFinal(dimensions = 1) private final Object[] hpyContextMembers;
935935

936+
/**
937+
* Few well known Python objects that are also HPyContext constants are guaranteed to always get
938+
* the same handle.
939+
*/
940+
static final int SINGLETON_HANDLE_NONE = 1;
941+
static final int SINGLETON_HANDLE_NOT_IMPLEMENTED = 2;
942+
static final int SINGLETON_HANDLE_ELIPSIS = 3;
943+
936944
/** the native type ID of C struct 'HPyContext' */
937945
@CompilationFinal private Object hpyContextNativeTypeID;
938946

@@ -972,20 +980,29 @@ public GraalHPyContext(PythonContext context, Object hpyLibrary) {
972980
int traceJNISleepTime = language.getEngineOption(PythonOptions.HPyTraceUpcalls);
973981
traceJNIUpcalls = traceJNISleepTime != 0;
974982
this.slowPathFactory = context.factory();
983+
nextHandle = GraalHPyBoxing.SINGLETON_HANDLE_MAX + 1;
984+
hpyHandleTable = new Object[IMMUTABLE_HANDLE_COUNT * 2];
985+
hpyHandleTable[0] = GraalHPyHandle.NULL_HANDLE_DELEGATE;
986+
// createMembers already assigns numeric handles to "singletons"
975987
this.hpyContextMembers = createMembers(context, T_NAME, traceJNIUpcalls);
988+
// This will assign handles to the remaining context constants
976989
for (Object member : hpyContextMembers) {
977990
if (member instanceof GraalHPyHandle) {
978991
GraalHPyHandle handle = (GraalHPyHandle) member;
979-
int id = handle.getId(this, ConditionProfile.getUncached());
992+
int id = handle.getIdUncached(this);
980993
assert id > 0 && id < IMMUTABLE_HANDLE_COUNT;
981-
994+
assert id > GraalHPyBoxing.SINGLETON_HANDLE_MAX ||
995+
getHPyHandleForObject(handle.getDelegate()) == id;
982996
}
983997
}
984-
hpyHandleTable = Arrays.copyOf(hpyHandleTable, IMMUTABLE_HANDLE_COUNT * 2);
985998
nextHandle = IMMUTABLE_HANDLE_COUNT;
986999
if (traceJNIUpcalls) {
9871000
startUpcallsDaemon(traceJNISleepTime);
9881001
}
1002+
1003+
assert getHPyHandleForObject(PNone.NONE) == SINGLETON_HANDLE_NONE;
1004+
assert getHPyHandleForObject(PEllipsis.INSTANCE) == SINGLETON_HANDLE_ELIPSIS;
1005+
assert getHPyHandleForObject(PNotImplemented.NOT_IMPLEMENTED) == SINGLETON_HANDLE_NOT_IMPLEMENTED;
9891006
}
9901007

9911008
public PythonObjectSlowPathFactory getSlowPathFactory() {
@@ -1744,7 +1761,6 @@ public long ctxAsStruct(long handle) {
17441761
return (long) HPyGetNativeSpacePointerNodeGen.getUncached().execute(receiver);
17451762
}
17461763

1747-
17481764
// Note: assumes that receiverHandle is not a boxed primitive value
17491765
@SuppressWarnings("try")
17501766
public final int ctxSetItems(long receiverHandle, String name, long valueHandle) {
@@ -2363,17 +2379,17 @@ Object invokeMember(String key, Object[] args,
23632379
return memberInvokeLib.execute(member, args);
23642380
}
23652381

2366-
private static Object[] createMembers(PythonContext context, TruffleString name, boolean traceJNIUpcalls) {
2382+
private Object[] createMembers(PythonContext context, TruffleString name, boolean traceJNIUpcalls) {
23672383
Object[] members = new Object[HPyContextMember.VALUES.length];
23682384

23692385
members[HPyContextMember.NAME.ordinal()] = new CStringWrapper(name);
23702386
createIntConstant(members, HPyContextMember.CTX_VERSION, 1);
23712387

2372-
createConstant(members, HPyContextMember.H_NONE, PNone.NONE);
2388+
createSingletonConstant(members, HPyContextMember.H_NONE, PNone.NONE, SINGLETON_HANDLE_NONE);
23732389
createConstant(members, HPyContextMember.H_TRUE, context.getTrue());
23742390
createConstant(members, HPyContextMember.H_FALSE, context.getFalse());
2375-
createConstant(members, HPyContextMember.H_NOTIMPLEMENTED, PNotImplemented.NOT_IMPLEMENTED);
2376-
createConstant(members, HPyContextMember.H_ELLIPSIS, PEllipsis.INSTANCE);
2391+
createSingletonConstant(members, HPyContextMember.H_NOTIMPLEMENTED, PNotImplemented.NOT_IMPLEMENTED, SINGLETON_HANDLE_NOT_IMPLEMENTED);
2392+
createSingletonConstant(members, HPyContextMember.H_ELLIPSIS, PEllipsis.INSTANCE, SINGLETON_HANDLE_ELIPSIS);
23772393

23782394
createTypeConstant(members, HPyContextMember.H_BASEEXCEPTION, context, PythonBuiltinClassType.PBaseException);
23792395
createTypeConstant(members, HPyContextMember.H_EXCEPTION, context, PythonBuiltinClassType.Exception);
@@ -2731,6 +2747,12 @@ private static void createConstant(Object[] members, HPyContextMember member, Ob
27312747
members[member.ordinal()] = GraalHPyHandle.create(value);
27322748
}
27332749

2750+
private void createSingletonConstant(Object[] members, HPyContextMember member, Object value, int handle) {
2751+
GraalHPyHandle graalHandle = GraalHPyHandle.createSingleton(value, handle);
2752+
members[member.ordinal()] = graalHandle;
2753+
hpyHandleTable[handle] = value;
2754+
}
2755+
27342756
private static void createTypeConstant(Object[] members, HPyContextMember member, Python3Core core, PythonBuiltinClassType value) {
27352757
members[member.ordinal()] = GraalHPyHandle.create(core.lookupType(value));
27362758
}
@@ -2795,6 +2817,21 @@ private int resizeHandleTable() {
27952817
}
27962818

27972819
public int getHPyHandleForObject(Object object) {
2820+
assert !(object instanceof GraalHPyHandle);
2821+
int singletonHandle = getHPyHandleForSingleton(object);
2822+
if (singletonHandle != -1) {
2823+
return singletonHandle;
2824+
}
2825+
return getHPyHandleForNonSingleton(object);
2826+
}
2827+
2828+
public static int getHPyHandleForSingleton(Object object) {
2829+
CompilerAsserts.neverPartOfCompilation();
2830+
assert !(object instanceof GraalHPyHandle);
2831+
return GraalHPyContextFactory.GetHPyHandleForSingletonNodeGen.getUncached().execute(object);
2832+
}
2833+
2834+
public int getHPyHandleForNonSingleton(Object object) {
27982835
assert !(object instanceof GraalHPyHandle);
27992836
// find free association
28002837

@@ -2821,6 +2858,32 @@ public int getHPyHandleForObject(Object object) {
28212858
return handle;
28222859
}
28232860

2861+
@GenerateUncached
2862+
public abstract static class GetHPyHandleForSingleton extends Node {
2863+
public abstract int execute(Object delegateObject);
2864+
2865+
@Specialization
2866+
static int doNone(PNone x) {
2867+
assert x == PNone.NONE;
2868+
return SINGLETON_HANDLE_NONE;
2869+
}
2870+
2871+
@Specialization
2872+
static int doElipsis(PEllipsis x) {
2873+
return SINGLETON_HANDLE_ELIPSIS;
2874+
}
2875+
2876+
@Specialization
2877+
static int doNotImplemented(PNotImplemented x) {
2878+
return SINGLETON_HANDLE_NOT_IMPLEMENTED;
2879+
}
2880+
2881+
@Fallback
2882+
static int doOthers(@SuppressWarnings("unused") Object delegate) {
2883+
return -1;
2884+
}
2885+
}
2886+
28242887
@TruffleBoundary
28252888
private void mirrorNativeSpacePointerToNative(Object delegate, int handleID) {
28262889
assert isPointer();

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

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,15 @@
4242
package com.oracle.graal.python.builtins.objects.cext.hpy;
4343

4444
import static com.oracle.graal.python.nodes.truffle.TruffleStringMigrationPythonTypes.assertNoJavaString;
45-
import static com.oracle.graal.python.util.PythonUtils.tsLiteral;
4645
import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING;
46+
import static com.oracle.graal.python.util.PythonUtils.tsLiteral;
4747

4848
import com.oracle.graal.python.PythonLanguage;
4949
import com.oracle.graal.python.builtins.objects.PNone;
5050
import com.oracle.graal.python.builtins.objects.PythonAbstractObject;
5151
import com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapperLibrary;
52+
import com.oracle.graal.python.builtins.objects.cext.hpy.GraalHPyContext.GetHPyHandleForSingleton;
53+
import com.oracle.graal.python.builtins.objects.cext.hpy.GraalHPyContextFactory.GetHPyHandleForSingletonNodeGen;
5254
import com.oracle.graal.python.runtime.PythonContext;
5355
import com.oracle.truffle.api.CompilerDirectives;
5456
import com.oracle.truffle.api.dsl.Cached;
@@ -64,8 +66,8 @@
6466
import com.oracle.truffle.api.library.ExportMessage;
6567
import com.oracle.truffle.api.nodes.Node;
6668
import com.oracle.truffle.api.profiles.ConditionProfile;
67-
import com.oracle.truffle.llvm.spi.NativeTypeLibrary;
6869
import com.oracle.truffle.api.strings.TruffleString;
70+
import com.oracle.truffle.llvm.spi.NativeTypeLibrary;
6971

7072
@ExportLibrary(InteropLibrary.class)
7173
@ExportLibrary(value = NativeTypeLibrary.class, useForAOT = false)
@@ -107,6 +109,11 @@ private GraalHPyHandle(Object delegate, int id) {
107109
this.id = id;
108110
}
109111

112+
public static GraalHPyHandle createSingleton(Object delegate, int handle) {
113+
assert handle <= GraalHPyBoxing.SINGLETON_HANDLE_MAX;
114+
return new GraalHPyHandle(delegate, handle);
115+
}
116+
110117
public static GraalHPyHandle create(Object delegate) {
111118
return new GraalHPyHandle(delegate);
112119
}
@@ -120,33 +127,34 @@ public static GraalHPyHandle createGlobal(Object delegate, int idx) {
120127
}
121128

122129
/**
123-
* This is basically like {@link #toNative(ConditionProfile, InteropLibrary)} but also returns
124-
* the ID.
130+
* This is basically like {@code toNative} but also returns the ID.
125131
*/
126-
public int getId(GraalHPyContext context, ConditionProfile hasIdProfile) {
127-
int result = id;
128-
if (!isPointer(hasIdProfile)) {
129-
assert !GraalHPyBoxing.isBoxablePrimitive(delegate) : "allocating handle for value that could be boxed";
130-
result = context.getHPyHandleForObject(delegate);
131-
id = result;
132-
}
133-
return result;
132+
public int getIdUncached(GraalHPyContext context) {
133+
return getId(context, ConditionProfile.getUncached(), GetHPyHandleForSingletonNodeGen.getUncached());
134134
}
135135

136-
public int getIdDebug(GraalHPyContext context) {
136+
public int getId(GraalHPyContext context, ConditionProfile hasIdProfile, GetHPyHandleForSingleton getSingletonNode) {
137137
int result = id;
138-
if (id == UNINITIALIZED) {
139-
result = context.getHPyHandleForObject(delegate);
138+
if (!isPointer(hasIdProfile)) {
139+
assert !GraalHPyBoxing.isBoxablePrimitive(delegate) : "allocating handle for value that could be boxed";
140+
result = getSingletonNode.execute(this.delegate);
141+
if (result == -1) {
142+
// profiled by the node
143+
result = context.getHPyHandleForNonSingleton(this.delegate);
144+
}
140145
id = result;
141146
}
147+
assert isValidId(this.delegate, result);
142148
return result;
143149
}
144150

145-
int getDebugId() {
146-
if (id >= 0) {
147-
return id;
151+
public boolean isValidId(Object obj, int id) {
152+
if (delegate == PNone.NO_VALUE) {
153+
// special case of HPy_NULL internally represented as NO_VALUE
154+
return id == 0;
148155
}
149-
return -id;
156+
int singletonId = GraalHPyContext.getHPyHandleForSingleton(obj);
157+
return singletonId == -1 || singletonId == id;
150158
}
151159

152160
@ExportMessage
@@ -179,11 +187,9 @@ long asPointer() throws UnsupportedMessageException {
179187
*/
180188
@ExportMessage
181189
void toNative(@Exclusive @Cached ConditionProfile isNativeProfile,
190+
@Cached GetHPyHandleForSingleton getSingletonNode,
182191
@CachedLibrary("this") InteropLibrary lib) {
183-
if (!isPointer(isNativeProfile)) {
184-
assert !GraalHPyBoxing.isBoxablePrimitive(delegate) : "allocating handle for value that could be boxed";
185-
id = PythonContext.get(lib).getHPyContext().getHPyHandleForObject(delegate);
186-
}
192+
getId(PythonContext.get(lib).getHPyContext(), isNativeProfile, getSingletonNode);
187193
}
188194

189195
@ExportMessage

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

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,11 @@
4444
import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING;
4545

4646
import com.oracle.graal.python.builtins.objects.cext.common.LoadCExtException.ApiInitException;
47+
import com.oracle.graal.python.builtins.objects.cext.hpy.GraalHPyContext.GetHPyHandleForSingleton;
4748
import com.oracle.graal.python.builtins.objects.cext.hpy.GraalHPyContext.HPyContextMember;
4849
import com.oracle.graal.python.builtins.objects.cext.hpy.GraalHPyContext.HPyContextNativePointer;
4950
import com.oracle.graal.python.builtins.objects.cext.hpy.GraalHPyContext.LLVMType;
51+
import com.oracle.graal.python.builtins.objects.cext.hpy.GraalHPyContextFactory.GetHPyHandleForSingletonNodeGen;
5052
import com.oracle.truffle.api.CompilerAsserts;
5153
import com.oracle.truffle.api.CompilerDirectives;
5254
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
@@ -440,6 +442,7 @@ static final class GraalHPyJNIConvertArgCachedNode extends GraalHPyJNIConvertArg
440442

441443
@Child private InteropLibrary interopLibrary;
442444
@CompilationFinal private ConditionProfile profile;
445+
@Child GetHPyHandleForSingleton getHPyHandleForSingleton;
443446

444447
@Override
445448
public long execute(Object[] arguments, int i) {
@@ -458,7 +461,7 @@ public long execute(Object[] arguments, int i) {
458461
assert delegate instanceof Double;
459462
return GraalHPyBoxing.boxDouble((Double) delegate);
460463
} else {
461-
return handle.getId(getHPyContext(arguments), ensureProfile());
464+
return handle.getId(getHPyContext(arguments), ensureProfile(), ensureHandleForSingletonNode());
462465
}
463466
} else if (value instanceof Long) {
464467
return (long) value;
@@ -485,6 +488,14 @@ private ConditionProfile ensureProfile() {
485488
}
486489
return profile;
487490
}
491+
492+
private GetHPyHandleForSingleton ensureHandleForSingletonNode() {
493+
if (getHPyHandleForSingleton == null) {
494+
CompilerDirectives.transferToInterpreterAndInvalidate();
495+
getHPyHandleForSingleton = insert(GetHPyHandleForSingletonNodeGen.create());
496+
}
497+
return getHPyHandleForSingleton;
498+
}
488499
}
489500

490501
static final class GraalHPyJNIConvertArgUncachedNode extends GraalHPyJNIConvertArgNode {
@@ -502,7 +513,7 @@ public long execute(Object[] arguments, int i) {
502513
assert delegate instanceof Double;
503514
return GraalHPyBoxing.boxDouble((Double) delegate);
504515
} else {
505-
return handle.getId(getHPyContext(arguments), ConditionProfile.getUncached());
516+
return handle.getIdUncached(getHPyContext(arguments));
506517
}
507518
} else if (value instanceof Long) {
508519
return (long) value;

0 commit comments

Comments
 (0)