Skip to content

Commit df2dda6

Browse files
committed
[GR-10764] specialization to bypass the MRO loop when the lookup and the attr value are constant
PullRequest: graalpython/109
2 parents 7f1b273 + 19e5baf commit df2dda6

File tree

50 files changed

+983
-454
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+983
-454
lines changed

graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/runtime/PythonModuleTests.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636

3737
import com.oracle.graal.python.PythonLanguage;
3838
import com.oracle.graal.python.builtins.objects.function.PArguments;
39+
import com.oracle.graal.python.builtins.objects.function.PKeyword;
3940
import com.oracle.graal.python.builtins.objects.method.PBuiltinMethod;
4041
import com.oracle.graal.python.builtins.objects.module.PythonModule;
4142
import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass;
@@ -66,15 +67,15 @@ public void pythonModuleTest() {
6667
public void builtinsMinTest() {
6768
final PythonModule builtins = context.getBuiltins();
6869
PBuiltinMethod min = (PBuiltinMethod) builtins.getAttribute(BuiltinNames.MIN);
69-
Object returnValue = InvokeNode.create(min).invoke(createWithUserArguments(builtins, 4, 2, 1));
70+
Object returnValue = InvokeNode.create(min).execute(null, createWithUserArguments(builtins, 4, 2, 1), PKeyword.EMPTY_KEYWORDS);
7071
assertEquals(1, returnValue);
7172
}
7273

7374
@Test
7475
public void builtinsIntTest() {
7576
final PythonModule builtins = context.getBuiltins();
7677
PythonBuiltinClass intClass = (PythonBuiltinClass) builtins.getAttribute(BuiltinNames.INT);
77-
Object returnValue = InvokeNode.create(intClass).invoke(createWithUserArguments(intClass, "42"));
78+
Object returnValue = InvokeNode.create(intClass).execute(null, createWithUserArguments(intClass, "42"), PKeyword.EMPTY_KEYWORDS);
7879
assertEquals(42, returnValue);
7980
}
8081

@@ -83,7 +84,7 @@ public void mainModuleTest() {
8384
PythonModule main = context.getMainModule();
8485
PythonModule builtins = (PythonModule) main.getAttribute(__BUILTINS__);
8586
PBuiltinMethod abs = (PBuiltinMethod) builtins.getAttribute(BuiltinNames.ABS);
86-
Object returned = InvokeNode.create(abs).invoke(createWithUserArguments(builtins, -42));
87+
Object returned = InvokeNode.create(abs).execute(null, createWithUserArguments(builtins, -42), PKeyword.EMPTY_KEYWORDS);
8788
assertEquals(42, returned);
8889
}
8990

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
2+
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3+
#
4+
# The Universal Permissive License (UPL), Version 1.0
5+
#
6+
# Subject to the condition set forth below, permission is hereby granted to any
7+
# person obtaining a copy of this software, associated documentation and/or
8+
# data (collectively the "Software"), free of charge and under any and all
9+
# copyright rights in the Software, and any and all patent rights owned or
10+
# freely licensable by each licensor hereunder covering either (i) the
11+
# unmodified Software as contributed to or provided by such licensor, or (ii)
12+
# the Larger Works (as defined below), to deal in both
13+
#
14+
# (a) the Software, and
15+
#
16+
# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
17+
# one is included with the Software each a "Larger Work" to which the Software
18+
# is contributed by such licensors),
19+
#
20+
# without restriction, including without limitation the rights to copy, create
21+
# derivative works of, display, perform, and distribute the Software and make,
22+
# use, sell, offer for sale, import, export, have made, and have sold the
23+
# Software and the Larger Work(s), and to sublicense the foregoing rights on
24+
# either these or other terms.
25+
#
26+
# This license is subject to the following condition:
27+
#
28+
# The above copyright notice and either this complete permission notice or at a
29+
# minimum a reference to the UPL must be included in all copies or substantial
30+
# portions of the Software.
31+
#
32+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38+
# SOFTWARE.
39+
40+
counter = 0
41+
42+
class Foo:
43+
def __setattr__(self, key, value):
44+
global counter
45+
counter = counter + 1
46+
object.__setattr__(self, key, value)
47+
def __delattr__(self, key):
48+
global counter
49+
counter = counter + 10
50+
object.__delattr__(self, key)
51+
52+
def test_call():
53+
global counter
54+
counter = 0
55+
f = Foo()
56+
f.a = 1
57+
Foo.b = 123
58+
assert counter == 1, "setting attrib on class should not call its own __setattr__"
59+
del f.a
60+
del Foo.b
61+
assert counter == 11, "deleting attrib on class should not call its own __delattr__"
62+
63+
class AClass:
64+
pass
65+
66+
class BClass(AClass):
67+
pass
68+
69+
class CClass(BClass):
70+
pass
71+
72+
class DClass(BClass):
73+
pass
74+
75+
def custom_set(self, key, value):
76+
object.__setattr__(self, key, value + 10 if isinstance(value, int) else value)
77+
78+
def custom_get(self, key):
79+
value = object.__getattribute__(self, key)
80+
return value + 100 if isinstance(value, int) else value
81+
82+
def test_assignments():
83+
object = CClass()
84+
# writing to BClass changes the result, writing to DClass doesn't
85+
targets = (AClass, BClass, DClass, CClass, object)
86+
results = (0, 1, 1, 3, 4)
87+
for i in range(0, len(targets)):
88+
targets[i].foo = i
89+
assert object.foo == results[i], "normal %d" %i
90+
# make sure that a custom __getattribute__ is used
91+
BClass.__getattribute__ = custom_get
92+
for i in range(0, len(targets)):
93+
targets[i].bar = i
94+
assert object.bar == results[i] + 100, "custom get %d" % i
95+
# check correct lookups when deleting attributes
96+
for i in reversed(range(0, len(targets))):
97+
assert object.bar == results[i] + 100, "delete %d" % i
98+
del targets[i].bar
99+
# make sure a custom __setattr__ is used
100+
BClass.__setattr__ = custom_set
101+
object.baz = 9
102+
assert object.baz == 119, "custom set"
103+
104+
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
2+
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3+
#
4+
# The Universal Permissive License (UPL), Version 1.0
5+
#
6+
# Subject to the condition set forth below, permission is hereby granted to any
7+
# person obtaining a copy of this software, associated documentation and/or
8+
# data (collectively the "Software"), free of charge and under any and all
9+
# copyright rights in the Software, and any and all patent rights owned or
10+
# freely licensable by each licensor hereunder covering either (i) the
11+
# unmodified Software as contributed to or provided by such licensor, or (ii)
12+
# the Larger Works (as defined below), to deal in both
13+
#
14+
# (a) the Software, and
15+
#
16+
# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
17+
# one is included with the Software each a "Larger Work" to which the Software
18+
# is contributed by such licensors),
19+
#
20+
# without restriction, including without limitation the rights to copy, create
21+
# derivative works of, display, perform, and distribute the Software and make,
22+
# use, sell, offer for sale, import, export, have made, and have sold the
23+
# Software and the Larger Work(s), and to sublicense the foregoing rights on
24+
# either these or other terms.
25+
#
26+
# This license is subject to the following condition:
27+
#
28+
# The above copyright notice and either this complete permission notice or at a
29+
# minimum a reference to the UPL must be included in all copies or substantial
30+
# portions of the Software.
31+
#
32+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38+
# SOFTWARE.
39+
40+
41+
def test_class_attr_change():
42+
class A(object):
43+
counter = 0
44+
45+
for i in range(10):
46+
A.counter += 1
47+
48+
assert A.counter == 10
49+
50+
51+
def test_class_attr_deleted():
52+
class A(object):
53+
counter = 0
54+
55+
class B(A):
56+
counter = 1
57+
58+
for i in range(10):
59+
B.counter += 1
60+
61+
assert B.counter == 11
62+
assert A.counter == 0
63+
del B.counter
64+
assert B.counter == 0
65+
66+
for i in range(10):
67+
A.counter += 1
68+
assert A.counter == 10
69+
70+
71+
def test_class_attr_added():
72+
class A(object):
73+
counter = 0
74+
75+
class B(A):
76+
pass
77+
78+
for i in range(10):
79+
B.counter += 1
80+
81+
assert B.counter == 10
82+
assert A.counter == 0
83+
B.counter = 1
84+
assert B.counter == 1
85+
86+
for i in range(10):
87+
A.counter += 1
88+
assert A.counter == 10
89+
90+
91+
def test_class_attr_add_del():
92+
class A:
93+
foo = 1
94+
95+
class B(A):
96+
foo = 2
97+
98+
class C(B):
99+
foo = 3
100+
101+
C.foo += 1
102+
C.foo += 1
103+
C.foo += 1
104+
C.foo += 1
105+
C.foo += 1
106+
C.foo += 1
107+
C.foo += 1
108+
109+
assert C.foo == 10
110+
del C.foo
111+
assert C.foo == 2
112+
del B.foo
113+
assert C.foo == 1
114+
B.foo = 5
115+
assert C.foo == 5
116+
C.foo = 10
117+
assert C.foo == 10
118+

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import com.oracle.graal.python.builtins.objects.PythonAbstractObject;
3838
import com.oracle.graal.python.builtins.objects.function.PArguments;
3939
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
40+
import com.oracle.graal.python.builtins.objects.function.PKeyword;
4041
import com.oracle.graal.python.builtins.objects.method.PBuiltinMethod;
4142
import com.oracle.graal.python.builtins.objects.module.PythonModule;
4243
import com.oracle.graal.python.builtins.objects.object.PythonObject;
@@ -356,7 +357,7 @@ protected String toString(PythonContext context, Object value) {
356357
Object[] userArgs = PArguments.create(2);
357358
PArguments.setArgument(userArgs, 0, PNone.NONE);
358359
PArguments.setArgument(userArgs, 1, value);
359-
Object res = InvokeNode.create(reprMethod).invoke(userArgs);
360+
Object res = InvokeNode.create(reprMethod).execute(null, userArgs, PKeyword.EMPTY_KEYWORDS);
360361
return res.toString();
361362
}
362363

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ protected AtExitCallTarget(TruffleLanguage<?> language, Object callable, Object[
8484

8585
@Override
8686
public Object execute(VirtualFrame frame) {
87-
return callNode.execute(callable, arguments, keywords);
87+
return callNode.execute(frame, callable, arguments, keywords);
8888
}
8989
}
9090

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

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@
159159
import com.oracle.truffle.api.dsl.GenerateNodeFactory;
160160
import com.oracle.truffle.api.dsl.Specialization;
161161
import com.oracle.truffle.api.dsl.TypeSystemReference;
162+
import com.oracle.truffle.api.frame.VirtualFrame;
162163
import com.oracle.truffle.api.nodes.UnexpectedResultException;
163164
import com.oracle.truffle.api.profiles.BranchProfile;
164165
import com.oracle.truffle.api.profiles.ConditionProfile;
@@ -1175,8 +1176,8 @@ public PList listObject(@SuppressWarnings("unused") Object cls, Object arg) {
11751176
@GenerateNodeFactory
11761177
public abstract static class ObjectNode extends PythonVarargsBuiltinNode {
11771178
@Override
1178-
public final Object varArgExecute(Object[] arguments, PKeyword[] keywords) throws VarargsBuiltinDirectInvocationNotSupported {
1179-
return execute(PNone.NO_VALUE, arguments, keywords);
1179+
public final Object varArgExecute(VirtualFrame frame, Object[] arguments, PKeyword[] keywords) throws VarargsBuiltinDirectInvocationNotSupported {
1180+
return execute(frame, PNone.NO_VALUE, arguments, keywords);
11801181
}
11811182

11821183
@Specialization
@@ -1500,8 +1501,7 @@ public Object type(Object cls, Object obj, PNone bases, PNone dict, PKeyword[] k
15001501
}
15011502

15021503
@Specialization(guards = {"!isNoValue(bases)", "!isNoValue(namespace)"})
1503-
@TruffleBoundary
1504-
public Object type(PythonClass cls, String name, PTuple bases, PDict namespace, PKeyword[] kwds,
1504+
public Object type(VirtualFrame frame, PythonClass cls, String name, PTuple bases, PDict namespace, PKeyword[] kwds,
15051505
@Cached("create()") GetClassNode getMetaclassNode,
15061506
@Cached("create(__NEW__)") LookupInheritedAttributeNode getNewFuncNode,
15071507
@Cached("create()") CallDispatchNode callNewFuncNode,
@@ -1512,9 +1512,14 @@ public Object type(PythonClass cls, String name, PTuple bases, PDict namespace,
15121512
if (newFunc instanceof PBuiltinFunction && (((PBuiltinFunction) newFunc).getFunctionRootNode() == getRootNode())) {
15131513
// the new metaclass has the same __new__ function as we are in
15141514
} else {
1515-
return callNewFuncNode.executeCall(newFunc, createArgs.execute(metaclass, name, bases, namespace), kwds);
1515+
return callNewFuncNode.executeCall(frame, newFunc, createArgs.execute(metaclass, name, bases, namespace), kwds);
15161516
}
15171517
}
1518+
return typeMetaclass(name, bases, namespace, metaclass);
1519+
}
1520+
1521+
@TruffleBoundary
1522+
private Object typeMetaclass(String name, PTuple bases, PDict namespace, PythonClass metaclass) {
15181523
if (name.indexOf('\0') != -1) {
15191524
throw raise(ValueError, "type name must not contain null characters");
15201525
}
@@ -1566,14 +1571,14 @@ private static boolean isSubType(PythonClass subclass, PythonClass superclass) {
15661571
return false;
15671572
}
15681573

1569-
protected abstract Object execute(Object cls, Object name, Object bases, Object dict, PKeyword[] kwds);
1574+
protected abstract Object execute(VirtualFrame frame, Object cls, Object name, Object bases, Object dict, PKeyword[] kwds);
15701575

15711576
protected static TypeNode create() {
15721577
return BuiltinConstructorsFactory.TypeNodeFactory.create(null);
15731578
}
15741579

15751580
@Specialization(guards = {"!isNoValue(bases)", "!isNoValue(dict)"})
1576-
public Object typeGeneric(Object cls, Object name, Object bases, Object dict, PKeyword[] kwds,
1581+
public Object typeGeneric(VirtualFrame frame, Object cls, Object name, Object bases, Object dict, PKeyword[] kwds,
15771582
@Cached("create()") TypeNode nextTypeNode) {
15781583
if (PGuards.isNoValue(bases) && !PGuards.isNoValue(dict) || !PGuards.isNoValue(bases) && PGuards.isNoValue(dict)) {
15791584
throw raise(TypeError, "type() takes 1 or 3 arguments");
@@ -1587,7 +1592,7 @@ public Object typeGeneric(Object cls, Object name, Object bases, Object dict, PK
15871592
// TODO: this is actually allowed, deal with it
15881593
throw raise(NotImplementedError, "creating a class with non-class metaclass");
15891594
}
1590-
return nextTypeNode.execute(cls, name, bases, dict, kwds);
1595+
return nextTypeNode.execute(frame, cls, name, bases, dict, kwds);
15911596
}
15921597
}
15931598

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,7 +1007,7 @@ public Object minmaxBinaryWithKey(Object arg1, Object[] args, PythonObject keywo
10071007
}
10081008

10091009
private static Object applyKeyFunction(PythonObject keywordArg, CallNode keyCall, Object currentValue) {
1010-
return keyCall == null ? currentValue : keyCall.execute(keywordArg, new Object[]{currentValue}, PKeyword.EMPTY_KEYWORDS);
1010+
return keyCall == null ? currentValue : keyCall.execute(null, keywordArg, new Object[]{currentValue}, PKeyword.EMPTY_KEYWORDS);
10111011
}
10121012
}
10131013

@@ -1145,7 +1145,7 @@ Object round(Object x, Object n,
11451145
public abstract static class SetAttrNode extends PythonBuiltinNode {
11461146
@Specialization
11471147
public Object setAttr(Object object, Object key, Object value,
1148-
@Cached("create()") SetAttributeNode setAttrNode) {
1148+
@Cached("new()") SetAttributeNode.Dynamic setAttrNode) {
11491149
return setAttrNode.execute(object, key, value);
11501150
}
11511151
}

0 commit comments

Comments
 (0)