Skip to content

Commit e7ecc4b

Browse files
committed
Add uncached fast-path for type.__call__ calls
1 parent 3f901ab commit e7ecc4b

File tree

3 files changed

+31
-2
lines changed

3 files changed

+31
-2
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@
172172
import com.oracle.truffle.api.dsl.GenerateCached;
173173
import com.oracle.truffle.api.dsl.GenerateInline;
174174
import com.oracle.truffle.api.dsl.GenerateNodeFactory;
175+
import com.oracle.truffle.api.dsl.GenerateUncached;
175176
import com.oracle.truffle.api.dsl.ImportStatic;
176177
import com.oracle.truffle.api.dsl.NeverDefault;
177178
import com.oracle.truffle.api.dsl.NodeFactory;
@@ -464,7 +465,7 @@ static Object init(@SuppressWarnings("unused") Object self, Object[] arguments,
464465
public abstract static class CallNode extends PythonVarargsBuiltinNode {
465466

466467
@Specialization
467-
Object call(VirtualFrame frame, Object self, Object[] arguments, PKeyword[] keywords,
468+
static Object call(VirtualFrame frame, Object self, Object[] arguments, PKeyword[] keywords,
468469
@Bind Node inliningTarget,
469470
@Cached IsSameTypeNode isSameTypeNode,
470471
@Cached GetClassNode getClassNode,
@@ -479,10 +480,16 @@ Object call(VirtualFrame frame, Object self, Object[] arguments, PKeyword[] keyw
479480
}
480481
return createInstanceNode.execute(frame, inliningTarget, self, arguments, keywords);
481482
}
483+
484+
public static Object executeUncached(VirtualFrame frame, Object self, Object[] arguments, PKeyword[] keywords) {
485+
return call(frame, self, arguments, keywords,
486+
null, IsSameTypeNode.getUncached(), GetClassNode.getUncached(), PRaiseNode.getUncached(), TypeBuiltinsFactory.CreateInstanceNodeGen.getUncached());
487+
}
482488
}
483489

484490
@GenerateInline
485491
@GenerateCached(false)
492+
@GenerateUncached
486493
protected abstract static class CreateInstanceNode extends PNodeWithContext {
487494

488495
abstract Object execute(VirtualFrame frame, Node inliningTarget, Object self, Object[] args, PKeyword[] keywords);

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1479,6 +1479,10 @@ public static IsSameTypeNode create() {
14791479
return IsSameTypeNodeGen.create();
14801480
}
14811481

1482+
public static IsSameTypeNode getUncached() {
1483+
return IsSameTypeNodeGen.getUncached();
1484+
}
1485+
14821486
@Specialization
14831487
static boolean doManaged(PythonManagedClass left, PythonManagedClass right) {
14841488
return left == right;

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

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import com.oracle.graal.python.builtins.objects.method.PBuiltinMethod;
4949
import com.oracle.graal.python.builtins.objects.type.TpSlots;
5050
import com.oracle.graal.python.builtins.objects.type.TpSlots.GetCachedTpSlotsNode;
51+
import com.oracle.graal.python.builtins.objects.type.TypeBuiltins;
5152
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotVarargs.CallSlotTpCallNode;
5253
import com.oracle.graal.python.nodes.ErrorMessages;
5354
import com.oracle.graal.python.nodes.PGuards;
@@ -185,7 +186,8 @@ static Object builtinMethodCall(VirtualFrame frame, PBuiltinMethod callable, Obj
185186
}
186187

187188
@Fallback
188-
static Object doGeneric(VirtualFrame frame, Object callableObject, Object[] arguments, PKeyword[] keywords,
189+
@SuppressWarnings("truffle-static-method")
190+
Object doGeneric(VirtualFrame frame, Object callableObject, Object[] arguments, PKeyword[] keywords,
189191
@Bind Node inliningTarget,
190192
@Cached PRaiseNode raise,
191193
@Cached GetClassNode getClassNode,
@@ -194,6 +196,22 @@ static Object doGeneric(VirtualFrame frame, Object callableObject, Object[] argu
194196
Object type = getClassNode.execute(inliningTarget, callableObject);
195197
TpSlots slots = getSlots.execute(inliningTarget, type);
196198
if (slots.tp_call() != null) {
199+
/*
200+
* Uncached fast-path for type.__call__. This is not just for performance, but has
201+
* "side-channel" effects. Without it, type.__call__'s needsCallerFrame assumption tends
202+
* to get invalidated, which then causes frame sync at every subsequent uncached
203+
* type.__call__ call. Frame syncs create new strong references to local variables (in
204+
* CPython as well) and these spurious frame syncs cause failures in weakref-using
205+
* tests.
206+
*/
207+
if (CompilerDirectives.inInterpreter() && !isAdoptable() && slots.tp_call() == TypeBuiltins.SLOTS.tp_call()) {
208+
/*
209+
* This node is often called from first-uncached bytecode execution, so it has the
210+
* frame despite being uncached. Passing it avoids stack walk when calling
211+
* super.__init__
212+
*/
213+
return TypeBuiltins.CallNode.executeUncached(frame, callableObject, arguments, keywords);
214+
}
197215
return callSlot.execute(frame, inliningTarget, slots.tp_call(), callableObject, arguments, keywords);
198216
}
199217
throw raise.raise(inliningTarget, TypeError, ErrorMessages.OBJ_ISNT_CALLABLE, callableObject);

0 commit comments

Comments
 (0)