Skip to content

Commit 2a30556

Browse files
committed
Implement C API symbol cache using an array
1 parent a1b2033 commit 2a30556

File tree

6 files changed

+45
-106
lines changed

6 files changed

+45
-106
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -364,9 +364,6 @@ public boolean isSingleContext() {
364364
@CompilationFinal(dimensions = 1) private volatile Object[] engineOptionsStorage;
365365
@CompilationFinal private volatile OptionValues engineOptions;
366366

367-
/** A shared shape for the C symbol cache (lazily initialized). */
368-
private Shape cApiSymbolCache;
369-
370367
/** For fast access to the PythonThreadState object by the owning thread. */
371368
private final ContextThreadLocal<PythonThreadState> threadState = locals.createContextThreadLocal(PythonContext.PythonThreadState::new);
372369

@@ -984,17 +981,6 @@ public Shape getBuiltinTypeInstanceShape(PythonBuiltinClassType type) {
984981
return shape;
985982
}
986983

987-
/**
988-
* Returns the shape used for the C API symbol cache.
989-
*/
990-
@TruffleBoundary
991-
public synchronized Shape getCApiSymbolCacheShape() {
992-
if (cApiSymbolCache == null) {
993-
cApiSymbolCache = Shape.newBuilder().build();
994-
}
995-
return cApiSymbolCache;
996-
}
997-
998984
/**
999985
* Cache call targets that are created for every new context, based on a single key.
1000986
*/

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextTypeBuiltins.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@
7979
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.SetterRoot;
8080
import com.oracle.graal.python.builtins.objects.cext.common.CArrayWrappers.CArrayWrapper;
8181
import com.oracle.graal.python.builtins.objects.cext.common.CExtContext;
82-
import com.oracle.graal.python.builtins.objects.cext.common.CExtContext.Store;
8382
import com.oracle.graal.python.builtins.objects.common.DynamicObjectStorage;
8483
import com.oracle.graal.python.builtins.objects.dict.PDict;
8584
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
@@ -120,6 +119,8 @@
120119
import com.oracle.truffle.api.library.CachedLibrary;
121120
import com.oracle.truffle.api.nodes.Node;
122121
import com.oracle.truffle.api.nodes.RootNode;
122+
import com.oracle.truffle.api.object.DynamicObject;
123+
import com.oracle.truffle.api.object.Shape;
123124
import com.oracle.truffle.api.profiles.InlinedExactClassProfile;
124125
import com.oracle.truffle.api.strings.TruffleString;
125126
import com.oracle.truffle.api.utilities.CyclicAssumption;
@@ -164,14 +165,20 @@ static Object doIt(PythonNativeClass self, TruffleString className) {
164165
}
165166
}
166167

168+
static final class NativeTypeDictStorage extends DynamicObject {
169+
public NativeTypeDictStorage(Shape shape) {
170+
super(shape);
171+
}
172+
}
173+
167174
@CApiBuiltin(ret = PyObjectTransfer, args = {PyTypeObject}, call = Ignored)
168175
abstract static class PyTruffle_NewTypeDict extends CApiUnaryBuiltinNode {
169176

170177
@Specialization
171178
@TruffleBoundary
172179
static PDict doGeneric(PythonNativeClass nativeClass) {
173180
PythonLanguage language = PythonLanguage.get(null);
174-
Store nativeTypeStore = new Store(language.getEmptyShape());
181+
NativeTypeDictStorage nativeTypeStore = new NativeTypeDictStorage(language.getEmptyShape());
175182
PDict dict = PythonObjectFactory.getUncached().createDict(new DynamicObjectStorage(nativeTypeStore));
176183
HiddenAttr.WriteNode.executeUncached(dict, HiddenAttr.INSTANCESHAPE, language.getShapeForClass(nativeClass));
177184
return dict;

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

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,6 @@
126126
import com.oracle.truffle.api.library.ExportLibrary;
127127
import com.oracle.truffle.api.library.ExportMessage;
128128
import com.oracle.truffle.api.nodes.Node;
129-
import com.oracle.truffle.api.object.DynamicObjectLibrary;
130-
import com.oracle.truffle.api.object.Shape;
131129
import com.oracle.truffle.api.source.Source;
132130
import com.oracle.truffle.api.source.Source.SourceBuilder;
133131
import com.oracle.truffle.api.strings.TruffleString;
@@ -201,6 +199,9 @@ public final class CApiContext extends CExtContext {
201199
public Object timezoneType;
202200
private PyCapsule pyDateTimeCAPICapsule;
203201

202+
/** A cache for C symbols. */
203+
private final Object[] nativeSymbolCache;
204+
204205
private record ClosureInfo(Object closure, Object delegate, Object executable, long pointer) {
205206
}
206207

@@ -239,6 +240,7 @@ public static TruffleLogger getLogger(Class<?> clazz) {
239240

240241
public CApiContext(PythonContext context, Object llvmLibrary, boolean useNativeBackend) {
241242
super(context, llvmLibrary, useNativeBackend);
243+
this.nativeSymbolCache = new Object[NativeCAPISymbol.values().length];
242244

243245
// initialize primitive native wrapper cache
244246
primitiveNativeWrapperCache = new PrimitiveNativeWrapper[262];
@@ -382,27 +384,32 @@ private void freeSmallInts() {
382384
}
383385
}
384386

385-
@TruffleBoundary
386-
@Override
387-
protected Store initializeSymbolCache() {
388-
PythonLanguage language = getContext().getLanguage();
389-
Shape symbolCacheShape = language.getCApiSymbolCacheShape();
390-
// We will always get an empty shape from the language and we do always add same key-value
391-
// pairs (in the same order). So, in the end, each context should get the same shape.
392-
Store s = new Store(symbolCacheShape);
393-
for (NativeCAPISymbol sym : NativeCAPISymbol.getValues()) {
394-
DynamicObjectLibrary.getUncached().put(s, sym, PNone.NO_VALUE);
395-
}
396-
return s;
397-
}
398-
399387
public Object getModuleByIndex(int i) {
400388
if (i < modulesByIndex.size()) {
401389
return modulesByIndex.get(i);
402390
}
403391
return null;
404392
}
405393

394+
public Object getNativeSymbol(NativeCAPISymbol symbol) {
395+
Object result = nativeSymbolCache[symbol.ordinal()];
396+
if (CompilerDirectives.injectBranchProbability(CompilerDirectives.SLOWPATH_PROBABILITY, result == null)) {
397+
result = lookupNativeSymbol(symbol);
398+
}
399+
return result;
400+
}
401+
402+
@TruffleBoundary
403+
private Object lookupNativeSymbol(NativeCAPISymbol symbol) {
404+
String name = symbol.getName();
405+
try {
406+
Object nativeSymbol = InteropLibrary.getUncached().readMember(getLLVMLibrary(), name);
407+
return nativeSymbolCache[symbol.ordinal()] = CExtContext.ensureExecutable(nativeSymbol, symbol);
408+
} catch (UnsupportedMessageException | UnknownIdentifierException e) {
409+
throw CompilerDirectives.shouldNotReachHere(e);
410+
}
411+
}
412+
406413
@TruffleBoundary
407414
public AllocInfo traceFree(Object ptr, @SuppressWarnings("unused") PFrame.Reference curFrame, @SuppressWarnings("unused") TruffleString clazzName) {
408415
if (allocatedNativeMemory == null) {

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

Lines changed: 13 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,10 @@
5959
import com.oracle.graal.python.builtins.objects.PNone;
6060
import com.oracle.graal.python.builtins.objects.bytes.BytesCommonBuiltins;
6161
import com.oracle.graal.python.builtins.objects.cext.PythonNativeVoidPtr;
62+
import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext;
6263
import com.oracle.graal.python.builtins.objects.cext.capi.CApiGuards;
6364
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.FromCharPointerNode;
65+
import com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol;
6466
import com.oracle.graal.python.builtins.objects.cext.capi.PrimitiveNativeWrapper;
6567
import com.oracle.graal.python.builtins.objects.cext.common.CArrayWrappers.CByteArrayWrapper;
6668
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodesFactory.GetIndexNodeGen;
@@ -93,7 +95,6 @@
9395
import com.oracle.graal.python.runtime.object.PythonObjectFactory;
9496
import com.oracle.graal.python.util.OverflowException;
9597
import com.oracle.graal.python.util.PythonUtils;
96-
import com.oracle.truffle.api.CompilerAsserts;
9798
import com.oracle.truffle.api.CompilerDirectives;
9899
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
99100
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
@@ -108,13 +109,10 @@
108109
import com.oracle.truffle.api.dsl.Specialization;
109110
import com.oracle.truffle.api.interop.InteropException;
110111
import com.oracle.truffle.api.interop.InteropLibrary;
111-
import com.oracle.truffle.api.interop.UnknownIdentifierException;
112112
import com.oracle.truffle.api.interop.UnsupportedMessageException;
113113
import com.oracle.truffle.api.library.CachedLibrary;
114114
import com.oracle.truffle.api.nodes.Node;
115115
import com.oracle.truffle.api.nodes.UnexpectedResultException;
116-
import com.oracle.truffle.api.object.DynamicObject;
117-
import com.oracle.truffle.api.object.DynamicObjectLibrary;
118116
import com.oracle.truffle.api.profiles.InlinedBranchProfile;
119117
import com.oracle.truffle.api.profiles.InlinedConditionProfile;
120118
import com.oracle.truffle.api.strings.TruffleString;
@@ -155,55 +153,22 @@ public static void fatalError(Node location, PythonContext context, String prefi
155153
@GenerateCached(false)
156154
public abstract static class ImportCExtSymbolNode extends PNodeWithContext {
157155

158-
public abstract Object execute(Node inliningTarget, CExtContext nativeContext, NativeCExtSymbol symbol);
159-
160-
@Specialization(guards = {"isSingleContext()", "cachedSymbol == symbol"}, limit = "1")
161-
static Object doSymbolCached(@SuppressWarnings("unused") Node inliningTarget, @SuppressWarnings("unused") CExtContext nativeContext, @SuppressWarnings("unused") NativeCExtSymbol symbol,
162-
@Cached("symbol") @SuppressWarnings("unused") NativeCExtSymbol cachedSymbol,
163-
@Cached("importCAPISymbolUncached(inliningTarget, nativeContext, symbol)") Object llvmSymbol) {
164-
return llvmSymbol;
156+
public final Object execute(Node inliningTarget, NativeCAPISymbol symbol) {
157+
return execute(inliningTarget, PythonContext.get(inliningTarget).getCApiContext(), symbol);
165158
}
166159

167-
// n.b. if 'singleContextAssumption' is valid, we may also cache the native context
168-
@Specialization(guards = {"isSingleContext()", "nativeContext == cachedNativeContext"}, limit = "1", //
169-
replaces = "doSymbolCached")
170-
static Object doWithSymbolCacheSingleContext(Node inliningTarget, @SuppressWarnings("unused") CExtContext nativeContext, NativeCExtSymbol symbol,
171-
@Cached("nativeContext") CExtContext cachedNativeContext,
172-
@Cached("nativeContext.getSymbolCache()") DynamicObject cachedSymbolCache,
173-
@CachedLibrary("cachedSymbolCache") DynamicObjectLibrary dynamicObjectLib) {
174-
return doWithSymbolCache(inliningTarget, cachedNativeContext, symbol, cachedSymbolCache, dynamicObjectLib);
175-
}
176-
177-
@Specialization(replaces = {"doSymbolCached", "doWithSymbolCacheSingleContext"}, limit = "1")
178-
static Object doWithSymbolCache(Node inliningTarget, CExtContext nativeContext, NativeCExtSymbol symbol,
179-
@Bind("nativeContext.getSymbolCache()") DynamicObject symbolCache,
180-
@CachedLibrary("symbolCache") DynamicObjectLibrary dynamicObjectLib) {
181-
Object nativeSymbol = dynamicObjectLib.getOrDefault(symbolCache, symbol, PNone.NO_VALUE);
182-
if (nativeSymbol == PNone.NO_VALUE) {
183-
nativeSymbol = importCAPISymbolUncached(inliningTarget, nativeContext, symbol, symbolCache, dynamicObjectLib);
184-
}
185-
return nativeSymbol;
186-
}
160+
public abstract Object execute(Node inliningTarget, CApiContext nativeContext, NativeCExtSymbol symbol);
187161

188-
protected static Object importCAPISymbolUncached(Node location, CExtContext nativeContext, NativeCExtSymbol symbol) {
189-
CompilerAsserts.neverPartOfCompilation();
190-
return importCAPISymbolUncached(location, nativeContext, symbol, nativeContext.getSymbolCache(), DynamicObjectLibrary.getUncached());
162+
@Specialization(guards = {"isSingleContext()", "cachedSymbol == symbol"}, limit = "1")
163+
static Object doCached(@SuppressWarnings("unused") Node inliningTarget, @SuppressWarnings("unused") CApiContext nativeContext, @SuppressWarnings("unused") NativeCAPISymbol symbol,
164+
@Cached("symbol") @SuppressWarnings("unused") NativeCAPISymbol cachedSymbol,
165+
@Cached(value = "nativeContext.getNativeSymbol(symbol)", weak = true) Object llvmSymbol) {
166+
return llvmSymbol;
191167
}
192168

193-
@TruffleBoundary
194-
protected static Object importCAPISymbolUncached(Node location, CExtContext nativeContext, NativeCExtSymbol symbol, DynamicObject symbolCache, DynamicObjectLibrary dynamicObjectLib) {
195-
Object llvmLibrary = nativeContext.getLLVMLibrary();
196-
String name = symbol.getName();
197-
try {
198-
Object nativeSymbol = InteropLibrary.getUncached().readMember(llvmLibrary, name);
199-
nativeSymbol = CExtContext.ensureExecutable(nativeSymbol, symbol);
200-
dynamicObjectLib.put(symbolCache, symbol, nativeSymbol);
201-
return nativeSymbol;
202-
} catch (UnknownIdentifierException e) {
203-
throw PRaiseNode.raiseUncached(location, PythonBuiltinClassType.SystemError, ErrorMessages.INVALID_CAPI_FUNC, symbol.getTsName());
204-
} catch (UnsupportedMessageException e) {
205-
throw PRaiseNode.raiseUncached(location, PythonBuiltinClassType.SystemError, ErrorMessages.CORRUPTED_CAPI_LIB_OBJ, llvmLibrary);
206-
}
169+
@Specialization(replaces = "doCached")
170+
static Object doGeneric(CApiContext nativeContext, NativeCAPISymbol symbol) {
171+
return nativeContext.getNativeSymbol(symbol);
207172
}
208173
}
209174

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

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,6 @@
8989
import com.oracle.truffle.api.interop.UnsupportedMessageException;
9090
import com.oracle.truffle.api.interop.UnsupportedTypeException;
9191
import com.oracle.truffle.api.nodes.Node;
92-
import com.oracle.truffle.api.object.DynamicObject;
93-
import com.oracle.truffle.api.object.Shape;
9492
import com.oracle.truffle.api.source.Source;
9593
import com.oracle.truffle.api.strings.TruffleString;
9694
import com.oracle.truffle.api.strings.TruffleString.CodeRange;
@@ -128,9 +126,6 @@ private static TruffleLogger getLogger() {
128126
/** The LLVM bitcode library object representing 'libpython.*.so' or similar. */
129127
private final Object llvmLibrary;
130128

131-
/** A cache for C symbols. */
132-
private DynamicObject symbolCache;
133-
134129
/**
135130
* The native API implementation was loaded as native code (as opposed to bitcode via Sulong).
136131
*/
@@ -186,21 +181,6 @@ public static boolean isClassOrStaticMethod(int flags) {
186181
return flags > 0 && (flags & (METH_CLASS | METH_STATIC)) != 0;
187182
}
188183

189-
public static final class Store extends DynamicObject {
190-
public Store(Shape shape) {
191-
super(shape);
192-
}
193-
}
194-
195-
public final DynamicObject getSymbolCache() {
196-
if (symbolCache == null) {
197-
symbolCache = initializeSymbolCache();
198-
}
199-
return symbolCache;
200-
}
201-
202-
protected abstract Store initializeSymbolCache();
203-
204184
/**
205185
* A simple helper object that just remembers the name and the path of the original module spec
206186
* object and also keeps a reference to it. This should avoid redundant attribute reads.

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

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1192,12 +1192,6 @@ private ReferenceQueue<Object> createReferenceQueue() {
11921192
return referenceQueue;
11931193
}
11941194

1195-
@Override
1196-
protected Store initializeSymbolCache() {
1197-
1198-
return null;
1199-
}
1200-
12011195
public int getCTypeSize(HPyContextSignatureType ctype) {
12021196
return backend.getCTypeSize(ctype);
12031197
}

0 commit comments

Comments
 (0)