Skip to content

Commit 996ac95

Browse files
lukasstadlertimfel
authored andcommitted
use "special calls" whenever possible to avoid dispatch overhead
1 parent 249c4aa commit 996ac95

File tree

3 files changed

+107
-42
lines changed

3 files changed

+107
-42
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GraalPythonModuleBuiltins.java

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -393,14 +393,6 @@ public synchronized PFunction convertToBuiltin(PFunction func) {
393393
* declare the module argument
394394
*/
395395
builtinFunc = func;
396-
func.getFunctionRootNode().accept(new NodeVisitor() {
397-
public boolean visit(Node node) {
398-
if (node instanceof PythonCallNode) {
399-
node.replace(((PythonCallNode) node).asSpecialCall());
400-
}
401-
return true;
402-
}
403-
});
404396
} else {
405397
/*
406398
* Otherwise, we create a new function with a signature that requires one extra
@@ -419,8 +411,6 @@ public boolean visit(Node node) {
419411
node.replace(ReadVarArgsNode.create(varArgsNode.getIndex() + 1, varArgsNode.isBuiltin()));
420412
} else if (node instanceof ReadIndexedArgumentNode) {
421413
node.replace(ReadIndexedArgumentNode.create(((ReadIndexedArgumentNode) node).getIndex() + 1));
422-
} else if (node instanceof PythonCallNode) {
423-
node.replace(((PythonCallNode) node).asSpecialCall());
424414
}
425415
return true;
426416
}

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

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2343,15 +2343,6 @@ abstract static class MakeMayRaiseWrapperNode extends PythonBuiltinNode {
23432343
Object make(PFunction func, Object errorResult,
23442344
@Exclusive @CachedLanguage PythonLanguage lang) {
23452345
CompilerDirectives.transferToInterpreter();
2346-
func.getFunctionRootNode().accept(new NodeVisitor() {
2347-
public boolean visit(Node node) {
2348-
if (node instanceof PythonCallNode) {
2349-
node.replace(((PythonCallNode) node).asSpecialCall());
2350-
}
2351-
return true;
2352-
}
2353-
});
2354-
23552346
RootNode rootNode = null;
23562347
Signature funcSignature = func.getSignature();
23572348
if (funcSignature.takesPositionalOnly()) {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/PythonCallNode.java

Lines changed: 107 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import com.oracle.graal.python.nodes.attributes.GetAttributeNode;
4040
import com.oracle.graal.python.nodes.attributes.GetAttributeNode.GetAnyAttributeNode;
4141
import com.oracle.graal.python.nodes.call.PythonCallNodeGen.GetCallAttributeNodeGen;
42+
import com.oracle.graal.python.nodes.call.PythonCallNodeGen.InvokeForeignNodeGen;
4243
import com.oracle.graal.python.nodes.call.special.CallBinaryMethodNode;
4344
import com.oracle.graal.python.nodes.call.special.CallQuaternaryMethodNode;
4445
import com.oracle.graal.python.nodes.call.special.CallTernaryMethodNode;
@@ -52,11 +53,14 @@
5253
import com.oracle.graal.python.nodes.object.GetClassNode;
5354
import com.oracle.graal.python.nodes.subscript.GetItemNode;
5455
import com.oracle.graal.python.nodes.util.ExceptionStateNodes.GetCaughtExceptionNode;
56+
import com.oracle.graal.python.runtime.PythonOptions;
5557
import com.oracle.graal.python.runtime.exception.PythonErrorType;
5658
import com.oracle.graal.python.runtime.object.PythonObjectFactory;
5759
import com.oracle.truffle.api.CompilerAsserts;
60+
import com.oracle.truffle.api.CompilerDirectives;
5861
import com.oracle.truffle.api.debug.DebuggerTags;
5962
import com.oracle.truffle.api.dsl.Cached;
63+
import com.oracle.truffle.api.dsl.ImportStatic;
6064
import com.oracle.truffle.api.dsl.NodeChild;
6165
import com.oracle.truffle.api.dsl.Specialization;
6266
import com.oracle.truffle.api.frame.VirtualFrame;
@@ -69,6 +73,7 @@
6973
import com.oracle.truffle.api.interop.UnsupportedMessageException;
7074
import com.oracle.truffle.api.interop.UnsupportedTypeException;
7175
import com.oracle.truffle.api.library.CachedLibrary;
76+
import com.oracle.truffle.api.nodes.Node;
7277
import com.oracle.truffle.api.profiles.BranchProfile;
7378

7479
@NodeChild("calleeNode")
@@ -95,7 +100,7 @@ public abstract class PythonCallNode extends ExpressionNode {
95100
this.keywordArguments = keywordArguments;
96101
}
97102

98-
public static PythonCallNode create(ExpressionNode calleeNode, ExpressionNode[] argumentNodes, ExpressionNode[] keywords, ExpressionNode starArgs, ExpressionNode kwArgs) {
103+
public static ExpressionNode create(ExpressionNode calleeNode, ExpressionNode[] argumentNodes, ExpressionNode[] keywords, ExpressionNode starArgs, ExpressionNode kwArgs) {
99104
assert !(starArgs instanceof EmptyNode) : "pass null instead";
100105
assert !(kwArgs instanceof EmptyNode) : "pass null instead";
101106

@@ -110,6 +115,22 @@ public static PythonCallNode create(ExpressionNode calleeNode, ExpressionNode[]
110115
getCallableNode = GetCallAttributeNodeGen.create(((GetAttributeNode) calleeNode).getKey(), ((GetAttributeNode) calleeNode).getObject());
111116
}
112117
KeywordArgumentsNode keywordArgumentsNode = kwArgs == null && keywords.length == 0 ? null : KeywordArgumentsNode.create(keywords, kwArgs);
118+
119+
if (argumentNodes != null && keywordArgumentsNode == null && starArgs == null) {
120+
switch (argumentNodes.length) {
121+
case 1:
122+
return new PythonCallUnary(getCallableNode, argumentNodes[0]);
123+
case 2:
124+
return new PythonCallBinary(getCallableNode, argumentNodes);
125+
case 3:
126+
return new PythonCallTernary(getCallableNode, argumentNodes);
127+
case 4:
128+
return new PythonCallQuaternary(getCallableNode, argumentNodes);
129+
default:
130+
// otherwise: fall through
131+
}
132+
}
133+
113134
if (starArgs == null) {
114135
return PythonCallNodeGen.create(calleeName, argumentNodes, null, keywordArgumentsNode, getCallableNode);
115136
} else {
@@ -126,14 +147,25 @@ private static class PythonCallUnary extends ExpressionNode {
126147
@Child ExpressionNode getCallable;
127148
@Child ExpressionNode argumentNode;
128149

150+
@Child InvokeForeign invokeForeign;
151+
129152
PythonCallUnary(ExpressionNode getCallable, ExpressionNode argumentNode) {
130153
this.getCallable = getCallable;
131154
this.argumentNode = argumentNode;
132155
}
133156

134157
@Override
135158
public Object execute(VirtualFrame frame) {
136-
return callUnary.executeObject(frame, getCallable.execute(frame), argumentNode.execute(frame));
159+
Object callable = getCallable.execute(frame);
160+
Object argument = argumentNode.execute(frame);
161+
if (callable instanceof ForeignInvoke) {
162+
if (invokeForeign == null) {
163+
CompilerDirectives.transferToInterpreterAndInvalidate();
164+
invokeForeign = insert(InvokeForeignNodeGen.create());
165+
}
166+
return invokeForeign.execute(frame, (ForeignInvoke) callable, new Object[]{argument}, PKeyword.EMPTY_KEYWORDS);
167+
}
168+
return callUnary.executeObject(frame, callable, argument);
137169
}
138170
}
139171

@@ -142,15 +174,26 @@ private static class PythonCallBinary extends ExpressionNode {
142174
@Child ExpressionNode getCallable;
143175
@Children final ExpressionNode[] argumentNodes;
144176

177+
@Child InvokeForeign invokeForeign;
178+
145179
PythonCallBinary(ExpressionNode getCallable, ExpressionNode[] argumentNodes) {
146180
this.getCallable = getCallable;
147181
this.argumentNodes = argumentNodes;
148182
}
149183

150184
@Override
151185
public Object execute(VirtualFrame frame) {
152-
Object[] evaluatedArguments = PositionalArgumentsNode.evaluateArguments(frame, argumentNodes);
153-
return callBinary.executeObject(frame, getCallable.execute(frame), evaluatedArguments[0], evaluatedArguments[1]);
186+
Object callable = getCallable.execute(frame);
187+
Object argument1 = argumentNodes[0].execute(frame);
188+
Object argument2 = argumentNodes[1].execute(frame);
189+
if (callable instanceof ForeignInvoke) {
190+
if (invokeForeign == null) {
191+
CompilerDirectives.transferToInterpreterAndInvalidate();
192+
invokeForeign = insert(InvokeForeignNodeGen.create());
193+
}
194+
return invokeForeign.execute(frame, (ForeignInvoke) callable, new Object[]{argument1, argument2}, PKeyword.EMPTY_KEYWORDS);
195+
}
196+
return callBinary.executeObject(frame, callable, argument1, argument2);
154197
}
155198
}
156199

@@ -159,14 +202,27 @@ private static class PythonCallTernary extends ExpressionNode {
159202
@Child ExpressionNode getCallable;
160203
@Children final ExpressionNode[] argumentNodes;
161204

205+
@Child InvokeForeign invokeForeign;
206+
162207
PythonCallTernary(ExpressionNode getCallable, ExpressionNode[] argumentNodes) {
163208
this.getCallable = getCallable;
164209
this.argumentNodes = argumentNodes;
165210
}
166211

167212
@Override
168213
public Object execute(VirtualFrame frame) {
169-
return callTernary.execute(frame, getCallable.execute(frame), argumentNodes[0].execute(frame), argumentNodes[1].execute(frame), argumentNodes[2].execute(frame));
214+
Object callable = getCallable.execute(frame);
215+
Object argument1 = argumentNodes[0].execute(frame);
216+
Object argument2 = argumentNodes[1].execute(frame);
217+
Object argument3 = argumentNodes[2].execute(frame);
218+
if (callable instanceof ForeignInvoke) {
219+
if (invokeForeign == null) {
220+
CompilerDirectives.transferToInterpreterAndInvalidate();
221+
invokeForeign = insert(InvokeForeignNodeGen.create());
222+
}
223+
return invokeForeign.execute(frame, (ForeignInvoke) callable, new Object[]{argument1, argument2, argument3}, PKeyword.EMPTY_KEYWORDS);
224+
}
225+
return callTernary.execute(frame, callable, argument1, argument2, argument3);
170226
}
171227
}
172228

@@ -175,15 +231,28 @@ private static class PythonCallQuaternary extends ExpressionNode {
175231
@Child ExpressionNode getCallable;
176232
@Children final ExpressionNode[] argumentNodes;
177233

234+
@Child InvokeForeign invokeForeign;
235+
178236
PythonCallQuaternary(ExpressionNode getCallable, ExpressionNode[] argumentNodes) {
179237
this.getCallable = getCallable;
180238
this.argumentNodes = argumentNodes;
181239
}
182240

183241
@Override
184242
public Object execute(VirtualFrame frame) {
185-
return callQuaternary.execute(frame, getCallable.execute(frame), argumentNodes[0].execute(frame), argumentNodes[1].execute(frame), argumentNodes[2].execute(frame),
186-
argumentNodes[3].execute(frame));
243+
Object callable = getCallable.execute(frame);
244+
Object argument1 = argumentNodes[0].execute(frame);
245+
Object argument2 = argumentNodes[1].execute(frame);
246+
Object argument3 = argumentNodes[2].execute(frame);
247+
Object argument4 = argumentNodes[3].execute(frame);
248+
if (callable instanceof ForeignInvoke) {
249+
if (invokeForeign == null) {
250+
CompilerDirectives.transferToInterpreterAndInvalidate();
251+
invokeForeign = insert(InvokeForeignNodeGen.create());
252+
}
253+
return invokeForeign.execute(frame, (ForeignInvoke) callable, new Object[]{argument1, argument2, argument3, argument4}, PKeyword.EMPTY_KEYWORDS);
254+
}
255+
return callQuaternary.execute(frame, callable, argument1, argument2, argument3, argument4);
187256
}
188257
}
189258

@@ -261,32 +330,47 @@ private PKeyword[] evaluateKeywords(VirtualFrame frame) {
261330
return keywordArguments == null ? PKeyword.EMPTY_KEYWORDS : keywordArguments.execute(frame);
262331
}
263332

333+
@ImportStatic({PythonOptions.class})
334+
abstract static class InvokeForeign extends Node {
335+
336+
@Child private CallNode callNode = CallNode.create();
337+
338+
public abstract Object execute(VirtualFrame frame, ForeignInvoke callable, Object[] arguments, PKeyword[] keywords);
339+
340+
@Specialization
341+
Object call(VirtualFrame frame, ForeignInvoke callable, Object[] arguments, PKeyword[] keywords,
342+
@Cached PRaiseNode raise,
343+
@Cached("create()") PForeignToPTypeNode fromForeign,
344+
@Cached("create()") BranchProfile typeError,
345+
@Cached("create()") BranchProfile invokeError,
346+
@Cached("create()") GetAnyAttributeNode getAttrNode,
347+
@CachedLibrary(limit = "getCallSiteInlineCacheMaxDepth()") InteropLibrary interop) {
348+
try {
349+
return fromForeign.executeConvert(interop.invokeMember(callable.receiver, callable.identifier, arguments));
350+
} catch (ArityException | UnsupportedTypeException e) {
351+
typeError.enter();
352+
throw raise.raise(PythonErrorType.TypeError, e);
353+
} catch (UnknownIdentifierException | UnsupportedMessageException e) {
354+
invokeError.enter();
355+
// the interop contract is to revert to readMember and then execute
356+
Object member = getAttrNode.executeObject(frame, callable.receiver, callable.identifier);
357+
return callNode.execute(frame, member, arguments, keywords);
358+
}
359+
}
360+
}
361+
264362
@Specialization
265363
Object call(VirtualFrame frame, ForeignInvoke callable,
266364
@Cached PRaiseNode raise,
267-
@Cached("create()") PForeignToPTypeNode fromForeign,
268365
@Cached("create()") BranchProfile keywordsError,
269-
@Cached("create()") BranchProfile typeError,
270-
@Cached("create()") BranchProfile invokeError,
271-
@Cached("create()") GetAnyAttributeNode getAttrNode,
272-
@CachedLibrary(limit = "getCallSiteInlineCacheMaxDepth()") InteropLibrary interop) {
366+
@Cached InvokeForeign invoke) {
273367
Object[] arguments = evaluateArguments(frame);
274368
PKeyword[] keywords = evaluateKeywords(frame);
275369
if (keywords.length != 0) {
276370
keywordsError.enter();
277371
throw raise.raise(PythonErrorType.TypeError, ErrorMessages.FOREIGN_INVOCATION_DOESNT_SUPPORT_KEYWORD_ARG);
278372
}
279-
try {
280-
return fromForeign.executeConvert(interop.invokeMember(callable.receiver, callable.identifier, arguments));
281-
} catch (ArityException | UnsupportedTypeException e) {
282-
typeError.enter();
283-
throw raise.raise(PythonErrorType.TypeError, e);
284-
} catch (UnknownIdentifierException | UnsupportedMessageException e) {
285-
invokeError.enter();
286-
// the interop contract is to revert to readMember and then execute
287-
Object member = getAttrNode.executeObject(frame, callable.receiver, callable.identifier);
288-
return callNode.execute(frame, member, arguments, keywords);
289-
}
373+
return invoke.execute(frame, callable, arguments, keywords);
290374
}
291375

292376
protected static boolean isSysExcInfo(Class<? extends PythonBuiltinBaseNode> nodeClass) {

0 commit comments

Comments
 (0)