Skip to content

Commit 0721fc3

Browse files
committed
also inline PBuiltinMethods in special calls if possible, and rewrite builtins and python_cext functions written in Python try to call builtins without creating a frame
1 parent 72ad4e6 commit 0721fc3

File tree

6 files changed

+156
-5
lines changed

6 files changed

+156
-5
lines changed

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@
118118
import com.oracle.graal.python.nodes.attributes.SetAttributeNode;
119119
import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode;
120120
import com.oracle.graal.python.nodes.call.CallNode;
121+
import com.oracle.graal.python.nodes.call.PythonCallNode;
121122
import com.oracle.graal.python.nodes.call.special.LookupAndCallBinaryNode;
122123
import com.oracle.graal.python.nodes.call.special.LookupAndCallTernaryNode;
123124
import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode;
@@ -832,15 +833,15 @@ private Object getId(PythonObject obj) {
832833
// isinstance(object, classinfo)
833834
@Builtin(name = ISINSTANCE, fixedNumOfPositionalArgs = 2)
834835
@GenerateNodeFactory
835-
public abstract static class IsInstanceNode extends PythonBuiltinNode {
836+
public abstract static class IsInstanceNode extends PythonBinaryBuiltinNode {
836837
@Child private GetClassNode getClassNode = GetClassNode.create();
837838
@Child private LookupAndCallBinaryNode instanceCheckNode = LookupAndCallBinaryNode.create(__INSTANCECHECK__);
838839
@Child private CastToBooleanNode castToBooleanNode = CastToBooleanNode.createIfTrueNode();
839840
@Child private TypeBuiltins.InstanceCheckNode typeInstanceCheckNode = TypeBuiltins.InstanceCheckNode.create();
840841
@Child private SequenceStorageNodes.LenNode lenNode;
841842

842843
public static IsInstanceNode create() {
843-
return BuiltinFunctionsFactory.IsInstanceNodeFactory.create(null);
844+
return BuiltinFunctionsFactory.IsInstanceNodeFactory.create();
844845
}
845846

846847
private boolean isInstanceCheckInternal(Object instance, Object cls) {
@@ -1410,6 +1411,8 @@ public boolean visit(Node node) {
14101411
node.replace(ReadVarArgsNode.create(varArgsNode.getIndex() + 1, varArgsNode.isBuiltin()));
14111412
} else if (node instanceof ReadIndexedArgumentNode) {
14121413
node.replace(ReadIndexedArgumentNode.create(((ReadIndexedArgumentNode) node).getIndex() + 1));
1414+
} else if (node instanceof PythonCallNode) {
1415+
node.replace(((PythonCallNode) node).asSpecialCall());
14131416
}
14141417
return true;
14151418
}

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

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
import com.oracle.graal.python.builtins.objects.function.Arity;
9090
import com.oracle.graal.python.builtins.objects.function.PArguments;
9191
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
92+
import com.oracle.graal.python.builtins.objects.function.PFunction;
9293
import com.oracle.graal.python.builtins.objects.function.PKeyword;
9394
import com.oracle.graal.python.builtins.objects.function.PythonCallable;
9495
import com.oracle.graal.python.builtins.objects.ints.PInt;
@@ -103,8 +104,8 @@
103104
import com.oracle.graal.python.builtins.objects.tuple.PTuple;
104105
import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass;
105106
import com.oracle.graal.python.builtins.objects.type.PythonClass;
106-
import com.oracle.graal.python.nodes.PNodeWithContext;
107107
import com.oracle.graal.python.nodes.PGuards;
108+
import com.oracle.graal.python.nodes.PNodeWithContext;
108109
import com.oracle.graal.python.nodes.SpecialAttributeNames;
109110
import com.oracle.graal.python.nodes.SpecialMethodNames;
110111
import com.oracle.graal.python.nodes.argument.CreateArgumentsNode;
@@ -114,7 +115,9 @@
114115
import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode;
115116
import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode;
116117
import com.oracle.graal.python.nodes.call.InvokeNode;
118+
import com.oracle.graal.python.nodes.call.PythonCallNode;
117119
import com.oracle.graal.python.nodes.expression.BinaryComparisonNode;
120+
import com.oracle.graal.python.nodes.function.FunctionRootNode;
118121
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
119122
import com.oracle.graal.python.nodes.function.PythonBuiltinNode;
120123
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
@@ -151,6 +154,7 @@
151154
import com.oracle.truffle.api.interop.UnsupportedTypeException;
152155
import com.oracle.truffle.api.nodes.DirectCallNode;
153156
import com.oracle.truffle.api.nodes.Node;
157+
import com.oracle.truffle.api.nodes.NodeVisitor;
154158
import com.oracle.truffle.api.nodes.RootNode;
155159
import com.oracle.truffle.api.profiles.BranchProfile;
156160
import com.oracle.truffle.api.profiles.ConditionProfile;
@@ -1801,8 +1805,17 @@ public Object execute(VirtualFrame frame) {
18011805
}
18021806

18031807
@Specialization
1804-
Object make(PythonCallable func, Object errorResult) {
1808+
Object make(PFunction func, Object errorResult) {
18051809
CompilerDirectives.transferToInterpreter();
1810+
FunctionRootNode functionRootNode = (FunctionRootNode) func.getFunctionRootNode();
1811+
func.getFunctionRootNode().accept(new NodeVisitor() {
1812+
public boolean visit(Node node) {
1813+
if (node instanceof PythonCallNode) {
1814+
node.replace(((PythonCallNode) node).asSpecialCall());
1815+
}
1816+
return true;
1817+
}
1818+
});
18061819
return factory().createBuiltinFunction(func.getName(), null, func.getArity(),
18071820
Truffle.getRuntime().createCallTarget(new MayRaiseWrapper(getRootNode().getLanguage(PythonLanguage.class), factory(), func, errorResult)));
18081821
}

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

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232
import com.oracle.graal.python.nodes.argument.positional.PositionalArgumentsNode;
3333
import com.oracle.graal.python.nodes.attributes.GetAttributeNode;
3434
import com.oracle.graal.python.nodes.call.PythonCallNodeGen.GetCallAttributeNodeGen;
35+
import com.oracle.graal.python.nodes.call.special.CallBinaryMethodNode;
36+
import com.oracle.graal.python.nodes.call.special.CallTernaryMethodNode;
37+
import com.oracle.graal.python.nodes.call.special.CallUnaryMethodNode;
3538
import com.oracle.graal.python.nodes.call.special.LookupAndCallBinaryNode;
3639
import com.oracle.graal.python.nodes.expression.ExpressionNode;
3740
import com.oracle.graal.python.nodes.frame.ReadGlobalOrBuiltinNode;
@@ -63,12 +66,14 @@ public abstract class PythonCallNode extends ExpressionNode {
6366
* Either "argument" or "positionalArgument" needs to be non-null (but not both), and
6467
* "keywordArguments" may be null.
6568
*/
66-
@Children private final ExpressionNode[] argumentNodes;
69+
@Children protected final ExpressionNode[] argumentNodes;
6770
@Child private PositionalArgumentsNode positionalArguments;
6871
@Child private KeywordArgumentsNode keywordArguments;
6972

7073
protected final String calleeName;
7174

75+
protected abstract ExpressionNode getCalleeNode();
76+
7277
PythonCallNode(String calleeName, ExpressionNode[] argumentNodes, PositionalArgumentsNode positionalArguments, KeywordArgumentsNode keywordArguments) {
7378
this.calleeName = calleeName;
7479
this.argumentNodes = argumentNodes;
@@ -96,6 +101,77 @@ public static PythonCallNode create(ExpressionNode calleeNode, ExpressionNode[]
96101
}
97102
}
98103

104+
private static class PythonCallUnary extends ExpressionNode {
105+
@Child CallUnaryMethodNode callUnary = CallUnaryMethodNode.create();
106+
@Child ExpressionNode getCallable;
107+
@Child ExpressionNode argumentNode;
108+
109+
PythonCallUnary(ExpressionNode getCallable, ExpressionNode argumentNode) {
110+
this.getCallable = getCallable;
111+
this.argumentNode = argumentNode;
112+
}
113+
114+
@Override
115+
public Object execute(VirtualFrame frame) {
116+
return callUnary.executeObject(getCallable.execute(frame), argumentNode.execute(frame));
117+
}
118+
}
119+
120+
private static class PythonCallBinary extends ExpressionNode {
121+
@Child CallBinaryMethodNode callBinary = CallBinaryMethodNode.create();
122+
@Child ExpressionNode getCallable;
123+
@Children final ExpressionNode[] argumentNodes;
124+
125+
PythonCallBinary(ExpressionNode getCallable, ExpressionNode[] argumentNodes) {
126+
this.getCallable = getCallable;
127+
this.argumentNodes = argumentNodes;
128+
}
129+
130+
@Override
131+
public Object execute(VirtualFrame frame) {
132+
Object[] evaluatedArguments = PositionalArgumentsNode.evaluateArguments(frame, argumentNodes);
133+
return callBinary.executeObject(getCallable.execute(frame), evaluatedArguments[0], evaluatedArguments[1]);
134+
}
135+
}
136+
137+
private static class PythonCallTernary extends ExpressionNode {
138+
@Child CallTernaryMethodNode callTernary = CallTernaryMethodNode.create();
139+
@Child ExpressionNode getCallable;
140+
@Children final ExpressionNode[] argumentNodes;
141+
142+
PythonCallTernary(ExpressionNode getCallable, ExpressionNode[] argumentNodes) {
143+
this.getCallable = getCallable;
144+
this.argumentNodes = argumentNodes;
145+
}
146+
147+
@Override
148+
public Object execute(VirtualFrame frame) {
149+
return callTernary.execute(getCallable.execute(frame), argumentNodes[0].execute(frame), argumentNodes[1].execute(frame), argumentNodes[2].execute(frame));
150+
}
151+
}
152+
153+
/**
154+
* If the argument length is fixed 1, 2, or 3 arguments, returns an expression node that uses
155+
* special call semantics, i.e., it can avoid creating a stack frame if the call target is a
156+
* builtin python function that takes 1, 2, or 3 arguments exactly. Otherwise, returns itself.
157+
*/
158+
public ExpressionNode asSpecialCall() {
159+
if (argumentNodes == null || keywordArguments != null) {
160+
return this;
161+
} else {
162+
switch (argumentNodes.length) {
163+
case 1:
164+
return new PythonCallUnary(getCalleeNode(), argumentNodes[0]);
165+
case 2:
166+
return new PythonCallBinary(getCalleeNode(), argumentNodes);
167+
case 3:
168+
return new PythonCallTernary(getCalleeNode(), argumentNodes);
169+
default:
170+
return this;
171+
}
172+
}
173+
}
174+
99175
@NodeChild("object")
100176
protected abstract static class GetCallAttributeNode extends ExpressionNode {
101177

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/CallBinaryMethodNode.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242

4343
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
4444
import com.oracle.graal.python.builtins.objects.function.PKeyword;
45+
import com.oracle.graal.python.builtins.objects.method.PBuiltinMethod;
4546
import com.oracle.graal.python.nodes.call.CallNode;
4647
import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode;
4748
import com.oracle.truffle.api.RootCallTarget;
@@ -191,6 +192,20 @@ Object callObject(@SuppressWarnings("unused") PBuiltinFunction func, Object arg1
191192
return builtinNode.execute(arg1, arg2);
192193
}
193194

195+
@Specialization(guards = {"func == cachedFunc", "builtinNode != null"}, limit = "getCallSiteInlineCacheMaxDepth()", assumptions = "singleContextAssumption()")
196+
Object callObject(@SuppressWarnings("unused") PBuiltinMethod func, Object arg1, Object arg2,
197+
@SuppressWarnings("unused") @Cached("func") PBuiltinMethod cachedFunc,
198+
@Cached("getBinary(func.getFunction())") PythonBinaryBuiltinNode builtinNode) {
199+
return builtinNode.execute(arg1, arg2);
200+
}
201+
202+
@Specialization(guards = {"func.getCallTarget() == ct", "builtinNode != null"}, limit = "getCallSiteInlineCacheMaxDepth()")
203+
Object callObject(@SuppressWarnings("unused") PBuiltinMethod func, Object arg1, Object arg2,
204+
@SuppressWarnings("unused") @Cached("func.getCallTarget()") RootCallTarget ct,
205+
@Cached("getBinary(func.getFunction())") PythonBinaryBuiltinNode builtinNode) {
206+
return builtinNode.execute(arg1, arg2);
207+
}
208+
194209
@Specialization
195210
Object call(Object func, Object arg1, Object arg2,
196211
@Cached("create()") CallNode callNode) {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/CallTernaryMethodNode.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242

4343
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
4444
import com.oracle.graal.python.builtins.objects.function.PKeyword;
45+
import com.oracle.graal.python.builtins.objects.method.PBuiltinMethod;
4546
import com.oracle.graal.python.nodes.call.CallNode;
4647
import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode;
4748
import com.oracle.truffle.api.RootCallTarget;
@@ -85,6 +86,34 @@ Object call(@SuppressWarnings("unused") PBuiltinFunction func, Object arg1, Obje
8586
return builtinNode.execute(arg1, arg2, arg3);
8687
}
8788

89+
@Specialization(guards = {"func == cachedFunc", "builtinNode != null"}, limit = "getCallSiteInlineCacheMaxDepth()")
90+
Object call(@SuppressWarnings("unused") PBuiltinMethod func, Object arg1, int arg2, Object arg3,
91+
@SuppressWarnings("unused") @Cached("func") PBuiltinMethod cachedFunc,
92+
@Cached("getTernary(func.getFunction())") PythonTernaryBuiltinNode builtinNode) {
93+
return builtinNode.execute(arg1, arg2, arg3);
94+
}
95+
96+
@Specialization(guards = {"func == cachedFunc", "builtinNode != null"}, limit = "getCallSiteInlineCacheMaxDepth()")
97+
Object call(@SuppressWarnings("unused") PBuiltinMethod func, Object arg1, Object arg2, Object arg3,
98+
@SuppressWarnings("unused") @Cached("func") PBuiltinMethod cachedFunc,
99+
@Cached("getTernary(func.getFunction())") PythonTernaryBuiltinNode builtinNode) {
100+
return builtinNode.execute(arg1, arg2, arg3);
101+
}
102+
103+
@Specialization(guards = {"func.getCallTarget() == ct", "builtinNode != null"}, limit = "getCallSiteInlineCacheMaxDepth()", assumptions = "singleContextAssumption()")
104+
Object call(@SuppressWarnings("unused") PBuiltinMethod func, Object arg1, int arg2, Object arg3,
105+
@SuppressWarnings("unused") @Cached("func.getCallTarget()") RootCallTarget ct,
106+
@Cached("getTernary(func.getFunction())") PythonTernaryBuiltinNode builtinNode) {
107+
return builtinNode.execute(arg1, arg2, arg3);
108+
}
109+
110+
@Specialization(guards = {"func.getCallTarget() == ct", "builtinNode != null"}, limit = "getCallSiteInlineCacheMaxDepth()", assumptions = "singleContextAssumption()")
111+
Object call(@SuppressWarnings("unused") PBuiltinMethod func, Object arg1, Object arg2, Object arg3,
112+
@SuppressWarnings("unused") @Cached("func.getCallTarget()") RootCallTarget ct,
113+
@Cached("getTernary(func.getFunction())") PythonTernaryBuiltinNode builtinNode) {
114+
return builtinNode.execute(arg1, arg2, arg3);
115+
}
116+
88117
@Specialization
89118
Object call(Object func, Object arg1, Object arg2, Object arg3,
90119
@Cached("create()") CallNode callNode) {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/CallUnaryMethodNode.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242

4343
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
4444
import com.oracle.graal.python.builtins.objects.function.PKeyword;
45+
import com.oracle.graal.python.builtins.objects.method.PBuiltinMethod;
4546
import com.oracle.graal.python.nodes.call.CallNode;
4647
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
4748
import com.oracle.truffle.api.RootCallTarget;
@@ -152,6 +153,20 @@ Object call(@SuppressWarnings("unused") PBuiltinFunction func, Object receiver,
152153
return builtinNode.execute(receiver);
153154
}
154155

156+
@Specialization(guards = {"func == cachedFunc", "builtinNode != null"}, limit = "getCallSiteInlineCacheMaxDepth()", assumptions = "singleContextAssumption()")
157+
Object callObjectSingleContext(@SuppressWarnings("unused") PBuiltinMethod func, Object receiver,
158+
@SuppressWarnings("unused") @Cached("func") PBuiltinMethod cachedFunc,
159+
@Cached("getUnary(func.getFunction())") PythonUnaryBuiltinNode builtinNode) {
160+
return builtinNode.execute(receiver);
161+
}
162+
163+
@Specialization(guards = {"func.getCallTarget() == ct", "builtinNode != null"}, limit = "getCallSiteInlineCacheMaxDepth()")
164+
Object call(@SuppressWarnings("unused") PBuiltinMethod func, Object receiver,
165+
@SuppressWarnings("unused") @Cached("func.getCallTarget()") RootCallTarget ct,
166+
@Cached("getUnary(func.getFunction())") PythonUnaryBuiltinNode builtinNode) {
167+
return builtinNode.execute(receiver);
168+
}
169+
155170
@Specialization
156171
Object call(Object func, Object receiver,
157172
@Cached("create()") CallNode callNode) {

0 commit comments

Comments
 (0)