Skip to content

Commit aee7793

Browse files
committed
[GR-52983] Avoid lots of upcalls in C API initialization.
PullRequest: graalpython/3276
2 parents e6f4794 + 4ea32dc commit aee7793

File tree

10 files changed

+146
-49
lines changed

10 files changed

+146
-49
lines changed

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

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -198,10 +198,9 @@ PY_TYPE_OBJECTS
198198
CAPI_BUILTINS
199199
#undef BUILTIN
200200

201-
PyAPI_FUNC(void) initialize_builtins(void* (*getBuiltin)(int id)) {
202-
int id = 0;
203-
//#define BUILTIN(NAME, RET, ...) printf("initializing " #NAME "\n"); Graal##NAME = (RET(*)(__VA_ARGS__)) getBuiltin(id++);
204-
#define BUILTIN(NAME, RET, ...) Graal##NAME = (RET(*)(__VA_ARGS__)) getBuiltin(id++);
201+
static inline void initialize_builtins(void *builtin_closures[]) {
202+
int id = 0;
203+
#define BUILTIN(NAME, RET, ...) Graal##NAME = (RET(*)(__VA_ARGS__)) builtin_closures[id++];
205204
CAPI_BUILTINS
206205
#undef BUILTIN
207206
}
@@ -817,15 +816,15 @@ void _PyFloat_InitState(PyInterpreterState* state);
817816
Py_LOCAL_SYMBOL TruffleContext* TRUFFLE_CONTEXT;
818817
Py_LOCAL_SYMBOL int graalpy_finalizing;
819818

820-
PyAPI_FUNC(void) initialize_graal_capi(TruffleEnv* env, void* (*getBuiltin)(int id)) {
821-
clock_t t = clock();
819+
PyAPI_FUNC(void) initialize_graal_capi(TruffleEnv* env, void **builtin_closures) {
820+
clock_t t = clock();
822821

823-
if (env) {
824-
TRUFFLE_CONTEXT = (*env)->getTruffleContext(env);
825-
}
822+
if (env) {
823+
TRUFFLE_CONTEXT = (*env)->getTruffleContext(env);
824+
}
826825

827-
initialize_builtins(getBuiltin);
828-
PyTruffle_Log(PY_TRUFFLE_LOG_FINE, "initialize_builtins: %fs", ((double) (clock() - t)) / CLOCKS_PER_SEC);
826+
initialize_builtins(builtin_closures);
827+
PyTruffle_Log(PY_TRUFFLE_LOG_FINE, "initialize_builtins: %fs", ((double) (clock() - t)) / CLOCKS_PER_SEC);
829828
Py_Truffle_Options = GraalPyTruffle_Native_Options();
830829

831830
initialize_builtin_types_and_structs();

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -724,26 +724,27 @@ void toNative() {
724724
try {
725725
SignatureContainerRootNode container = (SignatureContainerRootNode) context.signatureContainer.getRootNode();
726726
// create NFI closure and get its address
727-
boolean panama = PythonOptions.UsePanama.getValue(PythonContext.get(null).getEnv().getOptions());
727+
boolean panama = PythonOptions.UsePanama.getValue(context.getEnv().getOptions());
728728
StringBuilder signature = new StringBuilder(panama ? "with panama (" : "(");
729729
for (int i = 0; i < args.length; i++) {
730730
signature.append(i == 0 ? "" : ",");
731731
signature.append(args[i].getNFISignature());
732732
}
733733
signature.append("):").append(ret.getNFISignature());
734734

735-
Object nfiSignature = PythonContext.get(null).getEnv().parseInternal(Source.newBuilder(J_NFI_LANGUAGE, signature.toString(), "exec").build()).call();
735+
Object nfiSignature = context.getEnv().parseInternal(Source.newBuilder(J_NFI_LANGUAGE, signature.toString(), "exec").build()).call();
736736
Object closure = container.getLibrary(name).createClosure(nfiSignature, this);
737-
InteropLibrary.getUncached().toNative(closure);
737+
InteropLibrary lib = InteropLibrary.getUncached(closure);
738+
lib.toNative(closure);
738739
try {
739-
pointer = InteropLibrary.getUncached().asPointer(closure);
740+
pointer = lib.asPointer(closure);
740741
} catch (UnsupportedMessageException e) {
741742
throw CompilerDirectives.shouldNotReachHere(e);
742743
}
743744
context.getCApiContext().setClosurePointer(closure, null, this, pointer);
744745
LOGGER.finer(CApiBuiltinExecutable.class.getSimpleName() + " toNative: " + id + " / " + name() + " -> " + pointer);
745746
} catch (Throwable t) {
746-
t.printStackTrace(new PrintStream(PythonContext.get(null).getEnv().err()));
747+
t.printStackTrace(new PrintStream(context.getEnv().err()));
747748
throw t;
748749
}
749750
}

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

Lines changed: 101 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.HandleContext;
8383
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonNode;
8484
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.ToPythonWrapperNode;
85+
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes;
8586
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.CheckFunctionResultNode;
8687
import com.oracle.graal.python.builtins.objects.cext.common.CExtContext;
8788
import com.oracle.graal.python.builtins.objects.cext.common.LoadCExtException.ApiInitException;
@@ -127,6 +128,7 @@
127128
import com.oracle.truffle.api.frame.VirtualFrame;
128129
import com.oracle.truffle.api.interop.ArityException;
129130
import com.oracle.truffle.api.interop.InteropLibrary;
131+
import com.oracle.truffle.api.interop.InvalidArrayIndexException;
130132
import com.oracle.truffle.api.interop.TruffleObject;
131133
import com.oracle.truffle.api.interop.UnknownIdentifierException;
132134
import com.oracle.truffle.api.interop.UnsupportedMessageException;
@@ -807,12 +809,15 @@ public static CApiContext ensureCapiWasLoaded(Node node, PythonContext context,
807809
Object initFunction = U.readMember(capiLibrary, "initialize_graal_capi");
808810
CApiContext cApiContext = new CApiContext(context, capiLibrary, useNative);
809811
context.setCApiContext(cApiContext);
810-
if (!U.isExecutable(initFunction)) {
811-
Object signature = env.parseInternal(Source.newBuilder(J_NFI_LANGUAGE, "(ENV,(SINT32):POINTER):VOID", "exec").build()).call();
812-
initFunction = SignatureLibrary.getUncached().bind(signature, initFunction);
813-
U.execute(initFunction, new GetBuiltin());
814-
} else {
815-
U.execute(initFunction, NativePointer.createNull(), new GetBuiltin());
812+
try (BuiltinArrayWrapper builtinArrayWrapper = new BuiltinArrayWrapper()) {
813+
if (useNative) {
814+
Object signature = env.parseInternal(Source.newBuilder(J_NFI_LANGUAGE, "(ENV,(SINT32):POINTER):VOID", "exec").build()).call();
815+
initFunction = SignatureLibrary.getUncached().bind(signature, initFunction);
816+
U.execute(initFunction, builtinArrayWrapper);
817+
} else {
818+
assert U.isExecutable(initFunction);
819+
U.execute(initFunction, NativePointer.createNull(), builtinArrayWrapper);
820+
}
816821
}
817822

818823
assert PythonCApiAssertions.assertBuiltins(capiLibrary);
@@ -1051,29 +1056,48 @@ public PythonModule findExtension(TruffleString filename, TruffleString name) {
10511056
return extensions.get(Pair.create(filename, name));
10521057
}
10531058

1059+
/**
1060+
* An array wrapper around {@link PythonCextBuiltinRegistry#builtins} which also implements
1061+
* {@link InteropLibrary#toNative(Object)}. This is intended to be passed to the C API
1062+
* initialization function. In order to avoid memory leaks if the wrapper receives
1063+
* {@code toNative}, it should be used in a try-with-resources.
1064+
*/
10541065
@ExportLibrary(InteropLibrary.class)
1055-
static final class GetBuiltin implements TruffleObject {
1066+
@SuppressWarnings("static-method")
1067+
static final class BuiltinArrayWrapper implements TruffleObject, AutoCloseable {
1068+
private long pointer;
10561069

1057-
@SuppressWarnings("static-method")
10581070
@ExportMessage
1059-
boolean isExecutable() {
1071+
boolean hasArrayElements() {
10601072
return true;
10611073
}
10621074

1063-
@SuppressWarnings("static-method")
1075+
@ExportMessage
1076+
long getArraySize() {
1077+
return PythonCextBuiltinRegistry.builtins.length;
1078+
}
1079+
1080+
@ExportMessage
1081+
boolean isArrayElementReadable(long index) {
1082+
return 0 <= index && index < PythonCextBuiltinRegistry.builtins.length;
1083+
}
1084+
10641085
@ExportMessage
10651086
@TruffleBoundary
1066-
Object execute(Object[] arguments) {
1067-
assert arguments.length == 1;
1068-
int id = (int) arguments[0];
1087+
Object readArrayElement(long index) throws InvalidArrayIndexException {
1088+
if (!isArrayElementReadable(index)) {
1089+
throw InvalidArrayIndexException.create(index);
1090+
}
1091+
// cast is guaranteed by 'isArrayElementReadable'
1092+
return getCAPIBuiltinExecutable((int) index);
1093+
}
1094+
1095+
private static CApiBuiltinExecutable getCAPIBuiltinExecutable(int id) {
1096+
CompilerAsserts.neverPartOfCompilation();
10691097
try {
10701098
CApiBuiltinExecutable builtin = PythonCextBuiltinRegistry.builtins[id];
1071-
CApiContext cApiContext = PythonContext.get(null).getCApiContext();
1072-
if (cApiContext != null) {
1073-
Object llvmLibrary = cApiContext.getLLVMLibrary();
1074-
assert builtin.call() == CApiCallPath.Direct || !isAvailable(builtin, llvmLibrary) : "name clash in builtin vs. CAPI library: " + builtin.name();
1075-
}
1076-
LOGGER.finer("CApiContext.GetBuiltin " + id + " / " + builtin.name());
1099+
assert builtin.call() == CApiCallPath.Direct || !isAvailable(builtin) : "name clash in builtin vs. CAPI library: " + builtin.name();
1100+
LOGGER.finer("CApiContext.BuiltinArrayWrapper.get " + id + " / " + builtin.name());
10771101
return builtin;
10781102
} catch (Throwable e) {
10791103
// this is a fatal error, so print it to stderr:
@@ -1082,12 +1106,64 @@ Object execute(Object[] arguments) {
10821106
}
10831107
}
10841108

1085-
private static boolean isAvailable(CApiBuiltinExecutable builtin, Object llvmLibrary) {
1086-
if (!InteropLibrary.getUncached().isMemberReadable(llvmLibrary, builtin.name())) {
1109+
@ExportMessage
1110+
boolean isPointer() {
1111+
return pointer != 0;
1112+
}
1113+
1114+
@ExportMessage
1115+
long asPointer() throws UnsupportedMessageException {
1116+
if (pointer != 0) {
1117+
return pointer;
1118+
}
1119+
throw UnsupportedMessageException.create();
1120+
}
1121+
1122+
@ExportMessage
1123+
@TruffleBoundary
1124+
void toNative() {
1125+
if (pointer == 0) {
1126+
assert PythonContext.get(null).isNativeAccessAllowed();
1127+
Object ptr = CStructAccess.AllocateNode.callocUncached(PythonCextBuiltinRegistry.builtins.length, CStructAccess.POINTER_SIZE);
1128+
pointer = CExtCommonNodes.CoerceNativePointerToLongNode.executeUncached(ptr);
1129+
if (pointer != 0) {
1130+
InteropLibrary lib = null;
1131+
for (int i = 0; i < PythonCextBuiltinRegistry.builtins.length; i++) {
1132+
CApiBuiltinExecutable capiBuiltinExecutable = getCAPIBuiltinExecutable(i);
1133+
if (lib == null || !lib.accepts(capiBuiltinExecutable)) {
1134+
lib = InteropLibrary.getUncached(capiBuiltinExecutable);
1135+
}
1136+
assert lib.accepts(capiBuiltinExecutable);
1137+
lib.toNative(capiBuiltinExecutable);
1138+
try {
1139+
CStructAccess.WritePointerNode.writeArrayElementUncached(pointer, i, lib.asPointer(capiBuiltinExecutable));
1140+
} catch (UnsupportedMessageException e) {
1141+
throw CompilerDirectives.shouldNotReachHere(e);
1142+
}
1143+
}
1144+
}
1145+
}
1146+
}
1147+
1148+
@Override
1149+
public void close() {
1150+
if (pointer != 0) {
1151+
FreeNode.executeUncached(pointer);
1152+
}
1153+
}
1154+
1155+
private static boolean isAvailable(CApiBuiltinExecutable builtin) {
1156+
CApiContext cApiContext = PythonContext.get(null).getCApiContext();
1157+
if (cApiContext == null) {
1158+
return false;
1159+
}
1160+
Object llvmLibrary = cApiContext.getLLVMLibrary();
1161+
InteropLibrary lib = InteropLibrary.getUncached(llvmLibrary);
1162+
if (!lib.isMemberReadable(llvmLibrary, builtin.name())) {
10871163
return false;
10881164
}
10891165
try {
1090-
InteropLibrary.getUncached().readMember(llvmLibrary, builtin.name());
1166+
lib.readMember(llvmLibrary, builtin.name());
10911167
return true;
10921168
} catch (UnsupportedMessageException e) {
10931169
throw CompilerDirectives.shouldNotReachHere(e);
@@ -1122,14 +1198,14 @@ private static Source buildNFISource(Object srcObj) {
11221198
return Source.newBuilder(J_NFI_LANGUAGE, (String) srcObj, "exec").build();
11231199
}
11241200

1125-
public long registerClosure(String nfiSignature, Object executable, Object delegate) {
1201+
public long registerClosure(String nfiSignature, Object executable, Object delegate, SignatureLibrary signatureLibrary) {
11261202
CompilerAsserts.neverPartOfCompilation();
11271203
PythonContext context = getContext();
1128-
boolean panama = PythonOptions.UsePanama.getValue(context.getEnv().getOptions());
1204+
boolean panama = context.getOption(PythonOptions.UsePanama);
11291205
String srcString = (panama ? "with panama " : "") + nfiSignature;
11301206
Source nfiSource = context.getLanguage().getOrCreateSource(CApiContext::buildNFISource, srcString);
11311207
Object signature = context.getEnv().parseInternal(nfiSource).call();
1132-
Object closure = SignatureLibrary.getUncached().createClosure(signature, executable);
1208+
Object closure = signatureLibrary.createClosure(signature, executable);
11331209
long pointer = PythonUtils.coerceToLong(closure, InteropLibrary.getUncached());
11341210
setClosurePointer(closure, delegate, executable, pointer);
11351211
return pointer;

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1761,6 +1761,7 @@ static int doGeneric(CApiContext capiContext, PythonModule module, Object module
17611761
@Cached CStructAccess.ReadPointerNode readPointerNode,
17621762
@Cached CStructAccess.ReadI32Node readI32Node,
17631763
@CachedLibrary(limit = "3") InteropLibrary interopLib,
1764+
@CachedLibrary(limit = "1") SignatureLibrary signatureLibrary,
17641765
@Cached PRaiseNode raiseNode) {
17651766
InteropLibrary U = InteropLibrary.getUncached();
17661767
// call to type the pointer
@@ -1800,7 +1801,7 @@ static int doGeneric(CApiContext capiContext, PythonModule module, Object module
18001801
if (!U.isExecutable(execFunction)) {
18011802
boolean panama = context.getOption(PythonOptions.UsePanama);
18021803
Object signature = context.getEnv().parseInternal(panama ? NFI_PANAMA_EXEC : NFI_LIBFFI_EXEC).call();
1803-
execFunction = SignatureLibrary.getUncached().bind(signature, execFunction);
1804+
execFunction = signatureLibrary.bind(signature, execFunction);
18041805
}
18051806
Object result = interopLib.execute(execFunction, PythonToNativeNode.executeUncached(module));
18061807
int iResult = interopLib.asInt(result);

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,11 @@
6262
import com.oracle.truffle.api.dsl.Cached.Exclusive;
6363
import com.oracle.truffle.api.interop.ArityException;
6464
import com.oracle.truffle.api.interop.InteropLibrary;
65+
import com.oracle.truffle.api.library.CachedLibrary;
6566
import com.oracle.truffle.api.library.ExportLibrary;
6667
import com.oracle.truffle.api.library.ExportMessage;
6768
import com.oracle.truffle.api.nodes.Node;
69+
import com.oracle.truffle.nfi.api.SignatureLibrary;
6870

6971
/**
7072
* Wrappers for methods used by native code.
@@ -90,9 +92,11 @@ public long asPointer() {
9092

9193
@ExportMessage
9294
@TruffleBoundary
93-
public void toNative() {
95+
public void toNative(
96+
@CachedLibrary(limit = "1") SignatureLibrary signatureLibrary) {
9497
if (!isPointer()) {
95-
setNativePointer(PythonContext.get(null).getCApiContext().registerClosure(getSignature(), this, getDelegate()));
98+
CApiContext cApiContext = PythonContext.get(null).getCApiContext();
99+
setNativePointer(cApiContext.registerClosure(getSignature(), this, getDelegate(), signatureLibrary));
96100
}
97101
}
98102

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,12 @@
8383
import com.oracle.truffle.api.interop.TruffleObject;
8484
import com.oracle.truffle.api.interop.UnsupportedMessageException;
8585
import com.oracle.truffle.api.interop.UnsupportedTypeException;
86+
import com.oracle.truffle.api.library.CachedLibrary;
8687
import com.oracle.truffle.api.library.ExportLibrary;
8788
import com.oracle.truffle.api.library.ExportMessage;
8889
import com.oracle.truffle.api.nodes.Node;
8990
import com.oracle.truffle.api.strings.TruffleString;
91+
import com.oracle.truffle.nfi.api.SignatureLibrary;
9092

9193
/**
9294
* A wrapper class for managed functions such that they can be called with native function pointers
@@ -157,9 +159,11 @@ protected Object execute(Object[] arguments) throws UnsupportedTypeException, Ar
157159

158160
@ExportMessage
159161
@TruffleBoundary
160-
protected void toNative() {
162+
protected void toNative(
163+
@CachedLibrary(limit = "1") SignatureLibrary signatureLibrary) {
161164
if (pointer == 0) {
162-
pointer = PythonContext.get(null).getCApiContext().registerClosure(getSignature(), this, getDelegate());
165+
CApiContext cApiContext = PythonContext.get(null).getCApiContext();
166+
pointer = cApiContext.registerClosure(getSignature(), this, getDelegate(), signatureLibrary);
163167
}
164168
}
165169

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,12 @@
7979
import com.oracle.truffle.api.interop.InteropLibrary;
8080
import com.oracle.truffle.api.interop.UnsupportedMessageException;
8181
import com.oracle.truffle.api.interop.UnsupportedTypeException;
82+
import com.oracle.truffle.api.library.CachedLibrary;
8283
import com.oracle.truffle.api.library.ExportLibrary;
8384
import com.oracle.truffle.api.library.ExportMessage;
8485
import com.oracle.truffle.api.nodes.Node;
8586
import com.oracle.truffle.api.profiles.InlinedConditionProfile;
87+
import com.oracle.truffle.nfi.api.SignatureLibrary;
8688

8789
@ExportLibrary(InteropLibrary.class)
8890
public abstract class PyProcsWrapper extends PythonStructNativeWrapper {
@@ -119,9 +121,11 @@ protected long asPointer() {
119121

120122
@ExportMessage
121123
@TruffleBoundary
122-
protected void toNative() {
124+
protected void toNative(
125+
@CachedLibrary(limit = "1") SignatureLibrary signatureLibrary) {
123126
if (!isPointer()) {
124-
setNativePointer(PythonContext.get(null).getCApiContext().registerClosure(getSignature(), this, getDelegate()));
127+
CApiContext cApiContext = PythonContext.get(null).getCApiContext();
128+
setNativePointer(cApiContext.registerClosure(getSignature(), this, getDelegate(), signatureLibrary));
125129
}
126130
}
127131

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1136,6 +1136,10 @@ public static GetIndexNode create() {
11361136
@GenerateCached(false)
11371137
public abstract static class CoerceNativePointerToLongNode extends Node {
11381138

1139+
public static long executeUncached(Object pointerObject) {
1140+
return CExtCommonNodesFactory.CoerceNativePointerToLongNodeGen.getUncached().execute(null, pointerObject);
1141+
}
1142+
11391143
public abstract long execute(Node inliningTarget, Object pointerObject);
11401144

11411145
@Specialization

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1165,6 +1165,10 @@ public static void writeUncached(Object pointer, long offset, Object value) {
11651165
WritePointerNodeGen.getUncached().execute(pointer, offset, value);
11661166
}
11671167

1168+
public static void writeArrayElementUncached(long pointer, long element, long value) {
1169+
UNSAFE.putLong(pointer + element * POINTER_SIZE, value);
1170+
}
1171+
11681172
abstract void execute(Object pointer, long offset, Object value);
11691173

11701174
public final void write(Object pointer, CFields field, Object value) {

0 commit comments

Comments
 (0)