Skip to content

Commit 6857cb0

Browse files
committed
Split and quicken GetAttribute per type
1 parent ef52b5e commit 6857cb0

6 files changed

Lines changed: 448 additions & 7 deletions

File tree

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetFixedAttributeNode.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ Object doIt(VirtualFrame frame, Object object,
6666
@Bind Node inliningTarget,
6767
@Cached GetClassNode getClassNode,
6868
@Cached GetCachedTpSlotsNode getSlotsNode,
69-
@Cached MergedObjectTypeModuleGetFixedAttributeNode innerNode) {
69+
@Cached(inline = true) MergedObjectTypeModuleGetFixedAttributeNode innerNode) {
7070
Object type = getClassNode.execute(inliningTarget, object);
7171
TpSlots slots = getSlotsNode.execute(inliningTarget, type);
7272
return innerNode.execute(frame, inliningTarget, object, key, type, slots);
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*
2+
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package com.oracle.graal.python.nodes.attributes;
42+
43+
import static com.oracle.graal.python.nodes.attributes.MergedObjectTypeModuleGetFixedAttributeNode.hasNoGetAttr;
44+
45+
import com.oracle.graal.python.builtins.objects.PNone;
46+
import com.oracle.graal.python.builtins.objects.module.ModuleBuiltins;
47+
import com.oracle.graal.python.builtins.objects.module.PythonModule;
48+
import com.oracle.graal.python.builtins.objects.type.TpSlots.GetObjectSlotsNode;
49+
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot;
50+
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrGet.CallSlotDescrGet;
51+
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrSet;
52+
import com.oracle.graal.python.nodes.ErrorMessages;
53+
import com.oracle.graal.python.nodes.PNodeWithContext;
54+
import com.oracle.graal.python.nodes.PRaiseNode;
55+
import com.oracle.graal.python.runtime.exception.PException;
56+
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
57+
import com.oracle.truffle.api.dsl.Cached;
58+
import com.oracle.truffle.api.dsl.GenerateCached;
59+
import com.oracle.truffle.api.dsl.GenerateInline;
60+
import com.oracle.truffle.api.dsl.Specialization;
61+
import com.oracle.truffle.api.frame.VirtualFrame;
62+
import com.oracle.truffle.api.nodes.Node;
63+
import com.oracle.truffle.api.profiles.InlinedConditionProfile;
64+
import com.oracle.truffle.api.strings.TruffleString;
65+
66+
@GenerateInline
67+
@GenerateCached
68+
public abstract class GetFixedModuleAttributeNode extends PNodeWithContext {
69+
70+
public abstract Object execute(VirtualFrame frame, Node inliningTarget, Object object, TruffleString key, Object type);
71+
72+
/**
73+
* @see com.oracle.graal.python.builtins.objects.module.ModuleBuiltins.ModuleGetattributeNode
74+
*/
75+
@Specialization
76+
static Object doIt(VirtualFrame frame, Node inliningTarget, Object object, TruffleString key, Object type,
77+
@Cached(value = "create(key)", inline = false) LookupAttributeInMRONode lookup,
78+
@Cached GetObjectSlotsNode getDescrSlotsNode,
79+
@Cached ReadAttributeFromModuleNode readAttributeOfModuleNode,
80+
@Cached InlinedConditionProfile hasDescrProfile,
81+
@Cached InlinedConditionProfile hasDescrGetProfile,
82+
@Cached InlinedConditionProfile hasValueProfile,
83+
@Cached CallSlotDescrGet.Lazy callSlotDescrGet,
84+
@Cached ModuleBuiltins.LazyHandleGetattrExceptionNode handleException,
85+
@Cached PRaiseNode raiseNode) {
86+
assert hasNoGetAttr(type);
87+
88+
PythonModule module = (PythonModule) object;
89+
try {
90+
Object descr = lookup.execute(type);
91+
boolean hasDescr = hasDescrProfile.profile(inliningTarget, descr != PNone.NO_VALUE);
92+
93+
TpSlot get = null;
94+
boolean hasDescrGet = false;
95+
boolean getValue = true;
96+
if (hasDescr) {
97+
var descrSlots = getDescrSlotsNode.execute(inliningTarget, descr);
98+
get = descrSlots.tp_descr_get();
99+
hasDescrGet = hasDescrGetProfile.profile(inliningTarget, get != null);
100+
if (hasDescrGet && TpSlotDescrSet.PyDescr_IsData(descrSlots)) {
101+
// fall through to callSlotDescrGet below to avoid duplicating the call site
102+
getValue = false;
103+
}
104+
}
105+
106+
if (getValue) {
107+
Object value = readAttributeOfModuleNode.execute(module, key);
108+
if (hasValueProfile.profile(inliningTarget, value != PNone.NO_VALUE)) {
109+
return value;
110+
}
111+
}
112+
113+
if (hasDescr) {
114+
if (hasDescrGet) {
115+
return callSlotDescrGet.get(inliningTarget).execute(frame, inliningTarget, get, descr, module, type);
116+
} else {
117+
return descr;
118+
}
119+
}
120+
121+
throw raiseNode.raiseAttributeError(inliningTarget, ErrorMessages.OBJ_P_HAS_NO_ATTR_S, module, key);
122+
} catch (PException e) {
123+
return handleException(frame, inliningTarget, module, key, e, handleException);
124+
}
125+
}
126+
127+
@InliningCutoff
128+
private static Object handleException(VirtualFrame frame, Node inliningTarget, PythonModule object, TruffleString key, PException e,
129+
ModuleBuiltins.LazyHandleGetattrExceptionNode handleException) {
130+
return handleException.get(inliningTarget).execute(frame, object, key, e);
131+
}
132+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package com.oracle.graal.python.nodes.attributes;
42+
43+
import static com.oracle.graal.python.nodes.attributes.MergedObjectTypeModuleGetFixedAttributeNode.hasNoGetAttr;
44+
45+
import com.oracle.graal.python.builtins.objects.PNone;
46+
import com.oracle.graal.python.builtins.objects.type.TpSlots.GetObjectSlotsNode;
47+
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot;
48+
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrGet.CallSlotDescrGet;
49+
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrSet;
50+
import com.oracle.graal.python.nodes.ErrorMessages;
51+
import com.oracle.graal.python.nodes.PNodeWithContext;
52+
import com.oracle.graal.python.nodes.PRaiseNode;
53+
import com.oracle.truffle.api.dsl.Cached;
54+
import com.oracle.truffle.api.dsl.GenerateCached;
55+
import com.oracle.truffle.api.dsl.GenerateInline;
56+
import com.oracle.truffle.api.dsl.Specialization;
57+
import com.oracle.truffle.api.frame.VirtualFrame;
58+
import com.oracle.truffle.api.nodes.Node;
59+
import com.oracle.truffle.api.profiles.InlinedConditionProfile;
60+
import com.oracle.truffle.api.strings.TruffleString;
61+
62+
@GenerateInline
63+
@GenerateCached
64+
public abstract class GetFixedObjectAttributeNode extends PNodeWithContext {
65+
66+
public abstract Object execute(VirtualFrame frame, Node inliningTarget, Object object, TruffleString key, Object type);
67+
68+
/**
69+
* @see com.oracle.graal.python.builtins.objects.object.ObjectBuiltins.GetAttributeNode
70+
*/
71+
@Specialization
72+
static Object doIt(VirtualFrame frame, Node inliningTarget, Object object, TruffleString key, Object type,
73+
@Cached(value = "create(key)", inline = false) LookupAttributeInMRONode lookup,
74+
@Cached GetObjectSlotsNode getDescrSlotsNode,
75+
@Cached ReadAttributeFromObjectNode readAttributeOfObjectNode,
76+
@Cached InlinedConditionProfile hasDescrProfile,
77+
@Cached InlinedConditionProfile hasDescrGetProfile,
78+
@Cached InlinedConditionProfile hasValueProfile,
79+
@Cached CallSlotDescrGet.Lazy callSlotDescrGet,
80+
@Cached PRaiseNode raiseNode) {
81+
assert hasNoGetAttr(type);
82+
83+
Object descr = lookup.execute(type);
84+
boolean hasDescr = hasDescrProfile.profile(inliningTarget, descr != PNone.NO_VALUE);
85+
86+
TpSlot get = null;
87+
boolean hasDescrGet = false;
88+
boolean getValue = true;
89+
if (hasDescr) {
90+
var descrSlots = getDescrSlotsNode.execute(inliningTarget, descr);
91+
get = descrSlots.tp_descr_get();
92+
hasDescrGet = hasDescrGetProfile.profile(inliningTarget, get != null);
93+
if (hasDescrGet && TpSlotDescrSet.PyDescr_IsData(descrSlots)) {
94+
// fall through to callSlotDescrGet below to avoid duplicating the call site
95+
getValue = false;
96+
}
97+
}
98+
99+
if (getValue) {
100+
Object value = readAttributeOfObjectNode.execute(object, key);
101+
if (hasValueProfile.profile(inliningTarget, value != PNone.NO_VALUE)) {
102+
return value;
103+
}
104+
}
105+
106+
if (hasDescr) {
107+
if (hasDescrGet) {
108+
return callSlotDescrGet.get(inliningTarget).execute(frame, inliningTarget, get, descr, object, type);
109+
} else {
110+
return descr;
111+
}
112+
}
113+
114+
throw raiseNode.raiseAttributeError(inliningTarget, ErrorMessages.OBJ_P_HAS_NO_ATTR_S, object, key);
115+
}
116+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/*
2+
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package com.oracle.graal.python.nodes.attributes;
42+
43+
import static com.oracle.graal.python.nodes.attributes.MergedObjectTypeModuleGetFixedAttributeNode.hasNoGetAttr;
44+
45+
import com.oracle.graal.python.builtins.objects.PNone;
46+
import com.oracle.graal.python.builtins.objects.type.TpSlots.GetObjectSlotsNode;
47+
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot;
48+
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrGet.CallSlotDescrGet;
49+
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrSet;
50+
import com.oracle.graal.python.nodes.ErrorMessages;
51+
import com.oracle.graal.python.nodes.PNodeWithContext;
52+
import com.oracle.graal.python.nodes.PRaiseNode;
53+
import com.oracle.truffle.api.dsl.Cached;
54+
import com.oracle.truffle.api.dsl.GenerateCached;
55+
import com.oracle.truffle.api.dsl.GenerateInline;
56+
import com.oracle.truffle.api.dsl.Specialization;
57+
import com.oracle.truffle.api.frame.VirtualFrame;
58+
import com.oracle.truffle.api.nodes.Node;
59+
import com.oracle.truffle.api.profiles.InlinedBranchProfile;
60+
import com.oracle.truffle.api.profiles.InlinedConditionProfile;
61+
import com.oracle.truffle.api.strings.TruffleString;
62+
63+
@GenerateInline
64+
@GenerateCached
65+
public abstract class GetFixedTypeAttributeNode extends PNodeWithContext {
66+
67+
public abstract Object execute(VirtualFrame frame, Node inliningTarget, Object object, TruffleString key, Object type);
68+
69+
/**
70+
* @see com.oracle.graal.python.builtins.objects.module.ModuleBuiltins.ModuleGetattributeNode
71+
*/
72+
@Specialization
73+
static Object doIt(VirtualFrame frame, Node inliningTarget, Object object, TruffleString key, Object type,
74+
@Cached(value = "create(key)", inline = false) LookupAttributeInMRONode lookup,
75+
@Cached(value = "create(key)", inline = false) LookupAttributeInMRONode readAttributeOfClassNode,
76+
@Cached GetObjectSlotsNode getDescrSlotsNode,
77+
@Cached GetObjectSlotsNode getValueSlotsNode,
78+
@Cached InlinedConditionProfile hasDescrProfile,
79+
@Cached InlinedConditionProfile hasDescrGetProfile,
80+
@Cached InlinedConditionProfile hasValueProfile,
81+
@Cached InlinedBranchProfile hasNonDescriptorValueProfile,
82+
@Cached CallSlotDescrGet.Lazy callSlotDescrGet,
83+
@Cached CallSlotDescrGet.Lazy callSlotValueGet,
84+
@Cached PRaiseNode raiseNode) {
85+
assert hasNoGetAttr(type);
86+
87+
Object descr = lookup.execute(type);
88+
boolean hasDescr = hasDescrProfile.profile(inliningTarget, descr != PNone.NO_VALUE);
89+
90+
TpSlot get = null;
91+
boolean hasDescrGet = false;
92+
boolean getValue = true;
93+
if (hasDescr) {
94+
var descrSlots = getDescrSlotsNode.execute(inliningTarget, descr);
95+
get = descrSlots.tp_descr_get();
96+
hasDescrGet = hasDescrGetProfile.profile(inliningTarget, get != null);
97+
if (hasDescrGet && TpSlotDescrSet.PyDescr_IsData(descrSlots)) {
98+
// fall through to callSlotDescrGet below to avoid duplicating the call site
99+
getValue = false;
100+
}
101+
}
102+
103+
if (getValue) {
104+
Object value = readAttributeOfClassNode.execute(object);
105+
if (hasValueProfile.profile(inliningTarget, value != PNone.NO_VALUE)) {
106+
var valueGet = getValueSlotsNode.execute(inliningTarget, value).tp_descr_get();
107+
if (valueGet == null) {
108+
hasNonDescriptorValueProfile.enter(inliningTarget);
109+
return value;
110+
} else {
111+
return callSlotValueGet.get(inliningTarget).execute(frame, inliningTarget, valueGet, value, PNone.NO_VALUE, object);
112+
}
113+
}
114+
}
115+
116+
if (hasDescr) {
117+
if (hasDescrGet) {
118+
return callSlotDescrGet.get(inliningTarget).execute(frame, inliningTarget, get, descr, object, type);
119+
} else {
120+
return descr;
121+
}
122+
}
123+
124+
throw raiseNode.raiseAttributeError(inliningTarget, ErrorMessages.TYPE_N_HAS_NO_ATTR, object, key);
125+
}
126+
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/MergedObjectTypeModuleGetFixedAttributeNode.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@
8282
* inlining, but the caller is expected to keep its identity stable for the lifetime of the node.
8383
*/
8484
@GenerateInline
85-
@GenerateCached(false)
85+
@GenerateCached
8686
public abstract class MergedObjectTypeModuleGetFixedAttributeNode extends PNodeWithContext {
8787

8888
public abstract Object execute(VirtualFrame frame, Node inliningTarget, Object object, TruffleString key, Object type, TpSlots slots);

0 commit comments

Comments
 (0)