Skip to content

Commit fcdb611

Browse files
committed
Avoid a lot of upcalls in C API init
1 parent e6f4794 commit fcdb611

File tree

5 files changed

+122
-38
lines changed

5 files changed

+122
-38
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: 98 additions & 22 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) {
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);

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)