Skip to content

Commit 8a78fdd

Browse files
committed
[GR-31158] Refactor attr write for classes.
PullRequest: graalpython/1847
2 parents b7fa690 + 17e6d11 commit 8a78fdd

File tree

4 files changed

+111
-89
lines changed

4 files changed

+111
-89
lines changed

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

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,15 +209,32 @@ private boolean computeNeedsNativeAllocation() {
209209
@Override
210210
@TruffleBoundary
211211
public void setAttribute(Object key, Object value) {
212-
invalidateFinalAttribute(key);
213212
super.setAttribute(key, value);
213+
onAttributeUpdate(key, value);
214214
}
215215

216-
@TruffleBoundary
217-
public final void invalidateFinalAttribute(Object key) {
216+
/**
217+
* Fast-path check designed for PE code.
218+
*/
219+
public final boolean canSkipOnAttributeUpdate(String key, @SuppressWarnings("unused") Object value) {
220+
return !methodResolutionOrder.hasAttributeInMROFinalAssumptions() &&
221+
!SpecialMethodSlot.canBeSpecial(key);
222+
}
223+
224+
public final void onAttributeUpdate(Object key, Object value) {
225+
// In compilation: use a profile and call the String key overload
218226
CompilerAsserts.neverPartOfCompilation();
219227
if (key instanceof String) {
220-
methodResolutionOrder.invalidateAttributeInMROFinalAssumptions((String) key);
228+
onAttributeUpdate((String) key, value);
229+
}
230+
}
231+
232+
@TruffleBoundary
233+
public final void onAttributeUpdate(String key, Object value) {
234+
methodResolutionOrder.invalidateAttributeInMROFinalAssumptions(key);
235+
SpecialMethodSlot slot = SpecialMethodSlot.findSpecialSlot(key);
236+
if (slot != null) {
237+
SpecialMethodSlot.fixupSpecialMethodSlot(this, slot, value);
221238
}
222239
}
223240

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,12 @@
5555
@GenerateUncached
5656
public abstract class WriteAttributeToDynamicObjectNode extends ObjectAttributeNode {
5757

58-
public abstract boolean execute(Object primary, Object key, Object value);
59-
6058
public abstract boolean execute(Object primary, HiddenKey key, Object value);
6159

6260
public abstract boolean execute(Object primary, String key, Object value);
6361

62+
public abstract boolean execute(Object primary, Object key, Object value);
63+
6464
public static WriteAttributeToDynamicObjectNode create() {
6565
return WriteAttributeToDynamicObjectNodeGen.create();
6666
}
@@ -76,8 +76,8 @@ static boolean writeDirect(DynamicObject dynamicObject, String key, Object value
7676
return true;
7777
}
7878

79-
@Specialization(guards = "isHiddenKey(key)", limit = "getAttributeAccessInlineCacheMaxDepth()")
80-
static boolean writeDirectHidden(DynamicObject dynamicObject, Object key, Object value,
79+
@Specialization(limit = "getAttributeAccessInlineCacheMaxDepth()")
80+
static boolean writeDirectHidden(DynamicObject dynamicObject, HiddenKey key, Object value,
8181
@CachedLibrary("dynamicObject") DynamicObjectLibrary dylib) {
8282
dylib.put(dynamicObject, key, value);
8383
return true;

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

Lines changed: 80 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@
5050
import com.oracle.graal.python.builtins.objects.dict.PDict;
5151
import com.oracle.graal.python.builtins.objects.object.PythonObject;
5252
import com.oracle.graal.python.builtins.objects.object.PythonObjectLibrary;
53+
import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass;
54+
import com.oracle.graal.python.builtins.objects.type.PythonClass;
5355
import com.oracle.graal.python.builtins.objects.type.PythonManagedClass;
5456
import com.oracle.graal.python.builtins.objects.type.SpecialMethodSlot;
5557
import com.oracle.graal.python.builtins.objects.type.TypeNodes.IsTypeNode;
@@ -70,14 +72,17 @@
7072
import com.oracle.truffle.api.dsl.ImportStatic;
7173
import com.oracle.truffle.api.dsl.Specialization;
7274
import com.oracle.truffle.api.library.CachedLibrary;
73-
import com.oracle.truffle.api.nodes.Node;
75+
import com.oracle.truffle.api.object.DynamicObjectLibrary;
76+
import com.oracle.truffle.api.object.HiddenKey;
7477
import com.oracle.truffle.api.profiles.BranchProfile;
7578

7679
@ImportStatic(PythonOptions.class)
7780
public abstract class WriteAttributeToObjectNode extends ObjectAttributeNode {
7881

7982
public abstract boolean execute(Object primary, Object key, Object value);
8083

84+
public abstract boolean execute(Object primary, HiddenKey key, Object value);
85+
8186
public static WriteAttributeToObjectNode create() {
8287
return WriteAttributeToObjectNotTypeNodeGen.create();
8388
}
@@ -97,61 +102,97 @@ protected static boolean isAttrWritable(PythonObject self, Object key) {
97102
return (self.getShape().getFlags() & PythonObject.HAS_SLOTS_BUT_NO_DICT_FLAG) == 0;
98103
}
99104

100-
private static void handlePossiblePythonClass(HandlePythonClassProfiles profiles, PythonObject object, Object keyObj, Object value) {
101-
if (PythonManagedClass.isInstance(object)) {
102-
profiles.isManagedClass.enter();
103-
handlePythonClass(profiles, PythonManagedClass.cast(object), keyObj, value);
105+
private static String castKey(CastToJavaStringNode castNode, Object value) {
106+
try {
107+
return castNode.execute(value);
108+
} catch (CannotCastException ex) {
109+
throw CompilerDirectives.shouldNotReachHere(ex);
104110
}
105111
}
106112

107-
private static void handlePythonClass(HandlePythonClassProfiles profiles, PythonManagedClass object, Object keyObj, Object value) {
108-
String key = profiles.castKey(keyObj);
109-
if (key == null) {
110-
return;
111-
}
112-
object.invalidateFinalAttribute(key);
113-
// Note: we need to handle builtin classes here, because during initialization we are
114-
// setting attributes of some builtin types to Python functions (when given builtin method
115-
// is not intrinsified in Java)
116-
if (SpecialMethodSlot.canBeSpecial(key)) {
117-
profiles.isSpecialKey.enter();
118-
SpecialMethodSlot slot = SpecialMethodSlot.findSpecialSlot(key);
119-
if (slot != null) {
120-
SpecialMethodSlot.fixupSpecialMethodSlot(object, slot, value);
113+
@Specialization(guards = "isAttrWritable(object, key)", limit = "getAttributeAccessInlineCacheMaxDepth()")
114+
static boolean writeHiddenKeyToDynamicStorage(PythonObject object, HiddenKey key, Object value,
115+
@CachedLibrary("object.getStorage()") DynamicObjectLibrary dylib) {
116+
// HiddenKeys are always written to the storage and do not have any other special handling
117+
dylib.put(object.getStorage(), key, value);
118+
return true;
119+
}
120+
121+
@Specialization(guards = {"!isHiddenKey(key)", "!lib.hasDict(object)", "isAttrWritable(object, key)", "!isManagedClass(object)"}, limit = "1")
122+
static boolean writeToDynamicStorageNoType(PythonObject object, Object key, Object value,
123+
@Cached CastToJavaStringNode castToStrNode,
124+
@CachedLibrary("object") @SuppressWarnings("unused") PythonObjectLibrary lib,
125+
@CachedLibrary(limit = "getAttributeAccessInlineCacheMaxDepth()") DynamicObjectLibrary dylib) {
126+
// Objects w/o dict that are not classes do not have any special handling
127+
String strKey = castKey(castToStrNode, key);
128+
dylib.put(object.getStorage(), strKey, value);
129+
return true;
130+
}
131+
132+
@Specialization(guards = {"!isHiddenKey(key)", "!lib.hasDict(klass)", "isAttrWritable(klass, key)"}, limit = "1")
133+
static boolean writeToDynamicStorageBuiltinType(PythonBuiltinClass klass, Object key, Object value,
134+
@CachedLibrary("klass") @SuppressWarnings("unused") PythonObjectLibrary lib,
135+
@Cached CastToJavaStringNode castToStrNode,
136+
@Cached BranchProfile callAttrUpdate,
137+
@CachedLibrary(limit = "getAttributeAccessInlineCacheMaxDepth()") DynamicObjectLibrary dylib) {
138+
String strKey = castKey(castToStrNode, key);
139+
try {
140+
dylib.put(klass, strKey, value);
141+
return true;
142+
} finally {
143+
if (!klass.canSkipOnAttributeUpdate(strKey, value)) {
144+
callAttrUpdate.enter();
145+
klass.onAttributeUpdate(strKey, value);
121146
}
122147
}
123148
}
124149

125-
// write to the DynamicObject
126-
@Specialization(guards = {
127-
"isAttrWritable(object, key)",
128-
"isHiddenKey(key) || !lib.hasDict(object)"
129-
}, limit = "1")
130-
static boolean writeToDynamicStorage(PythonObject object, Object key, Object value,
131-
@CachedLibrary("object") @SuppressWarnings("unused") PythonObjectLibrary lib,
132-
@Cached WriteAttributeToDynamicObjectNode writeAttributeToDynamicObjectNode,
133-
@Exclusive @Cached HandlePythonClassProfiles handlePythonClassProfiles) {
150+
static boolean[] createFlag() {
151+
return new boolean[1];
152+
}
153+
154+
@Specialization(guards = {"!isHiddenKey(key)", "!lib.hasDict(klass)", "isAttrWritable(klass, key)"}, limit = "1")
155+
static boolean writeToDynamicStoragePythonClass(PythonClass klass, Object key, Object value,
156+
@CachedLibrary("klass") @SuppressWarnings("unused") PythonObjectLibrary lib,
157+
@Cached CastToJavaStringNode castToStrNode,
158+
@Cached BranchProfile callAttrUpdate,
159+
@CachedLibrary(limit = "getAttributeAccessInlineCacheMaxDepth()") DynamicObjectLibrary dylib) {
160+
String strKey = castKey(castToStrNode, key);
134161
try {
135-
return writeAttributeToDynamicObjectNode.execute(object.getStorage(), key, value);
162+
dylib.put(klass, strKey, value);
163+
return true;
136164
} finally {
137-
handlePossiblePythonClass(handlePythonClassProfiles, object, key, value);
165+
if (!klass.canSkipOnAttributeUpdate(strKey, value)) {
166+
callAttrUpdate.enter();
167+
klass.onAttributeUpdate(strKey, value);
168+
}
138169
}
139170
}
140171

141172
// write to the dict
142-
@Specialization(guards = {
143-
"!isHiddenKey(key)",
144-
"lib.hasDict(object)"
145-
}, limit = "1")
146-
static boolean writeToDict(PythonObject object, Object key, Object value,
173+
@Specialization(guards = {"!isHiddenKey(key)", "lib.hasDict(object)", "!isManagedClass(object)"}, limit = "1")
174+
static boolean writeToDictNoType(PythonObject object, Object key, Object value,
147175
@CachedLibrary("object") PythonObjectLibrary lib,
148176
@Cached BranchProfile updateStorage,
149-
@CachedLibrary(limit = "1") HashingStorageLibrary hlib,
150-
@Exclusive @Cached HandlePythonClassProfiles handlePythonClassProfiles) {
177+
@CachedLibrary(limit = "1") HashingStorageLibrary hlib) {
178+
return writeToDict(lib.getDict(object), key, value, updateStorage, hlib);
179+
}
180+
181+
@Specialization(guards = {"!isHiddenKey(key)", "lib.hasDict(klass)"}, limit = "1")
182+
static boolean writeToDictBuiltinType(PythonManagedClass klass, Object key, Object value,
183+
@Cached CastToJavaStringNode castToStrNode,
184+
@Cached BranchProfile callAttrUpdate,
185+
@CachedLibrary("klass") PythonObjectLibrary lib,
186+
@Cached BranchProfile updateStorage,
187+
@CachedLibrary(limit = "1") HashingStorageLibrary hlib) {
188+
String strKey = castKey(castToStrNode, key);
151189
try {
152-
return writeToDict(lib.getDict(object), key, value, updateStorage, hlib);
190+
return writeToDict(lib.getDict(klass), strKey, value, updateStorage, hlib);
153191
} finally {
154-
handlePossiblePythonClass(handlePythonClassProfiles, object, key, value);
192+
if (!klass.canSkipOnAttributeUpdate(strKey, value)) {
193+
callAttrUpdate.enter();
194+
klass.onAttributeUpdate(strKey, value);
195+
}
155196
}
156197
}
157198

@@ -277,46 +318,4 @@ private String castKey(Object keyObj) {
277318
return castKeyNode.execute(keyObj);
278319
}
279320
}
280-
281-
protected static final class HandlePythonClassProfiles extends Node {
282-
private static final HandlePythonClassProfiles UNCACHED = new HandlePythonClassProfiles(BranchProfile.getUncached(), BranchProfile.getUncached(), BranchProfile.getUncached(),
283-
CastToJavaStringNode.getUncached());
284-
final BranchProfile isManagedClass;
285-
final BranchProfile isUserClass;
286-
final BranchProfile isSpecialKey;
287-
@Child CastToJavaStringNode castKeyNode;
288-
289-
public HandlePythonClassProfiles(BranchProfile isManagedClass, BranchProfile isUserClass, BranchProfile isSpecialKey, CastToJavaStringNode castKeyNode) {
290-
this.isManagedClass = isManagedClass;
291-
this.isUserClass = isUserClass;
292-
this.isSpecialKey = isSpecialKey;
293-
this.castKeyNode = castKeyNode;
294-
}
295-
296-
public static HandlePythonClassProfiles create() {
297-
return new HandlePythonClassProfiles(BranchProfile.create(), BranchProfile.create(), BranchProfile.create(), null);
298-
}
299-
300-
public static HandlePythonClassProfiles getUncached() {
301-
return UNCACHED;
302-
}
303-
304-
String castKey(Object key) {
305-
if (castKeyNode == null) {
306-
// fast-path w/o node for two most common situations
307-
if (key instanceof String) {
308-
return (String) key;
309-
} else if (isHiddenKey(key)) {
310-
return null;
311-
}
312-
CompilerDirectives.transferToInterpreterAndInvalidate();
313-
castKeyNode = insert(CastToJavaStringNode.create());
314-
}
315-
try {
316-
return castKeyNode.execute(key);
317-
} catch (CannotCastException ex) {
318-
return null;
319-
}
320-
}
321-
}
322321
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/sequence/storage/MroSequenceStorage.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ public final class MroSequenceStorage extends TypedSequenceStorage {
7171
* assumptions will be invalidated if the mro changes.
7272
*/
7373
private final Map<String, List<Assumption>> attributesInMROFinalAssumptions;
74+
private boolean hasAttributesInMROFinalAssumptions;
7475

7576
@CompilationFinal(dimensions = 1) private final PythonAbstractClass[] values;
7677

@@ -228,6 +229,7 @@ public Assumption createAttributeInMROFinalAssumption(String name) {
228229
List<Assumption> attrAssumptions = attributesInMROFinalAssumptions.getOrDefault(name, null);
229230
if (attrAssumptions == null) {
230231
attrAssumptions = new ArrayList<>();
232+
hasAttributesInMROFinalAssumptions = true;
231233
attributesInMROFinalAssumptions.put(name, attrAssumptions);
232234
}
233235

@@ -241,6 +243,7 @@ public void addAttributeInMROFinalAssumption(String name, Assumption assumption)
241243
List<Assumption> attrAssumptions = attributesInMROFinalAssumptions.getOrDefault(name, null);
242244
if (attrAssumptions == null) {
243245
attrAssumptions = new ArrayList<>();
246+
hasAttributesInMROFinalAssumptions = true;
244247
attributesInMROFinalAssumptions.put(name, attrAssumptions);
245248
}
246249

@@ -301,4 +304,7 @@ public Object[] getCopyOfInternalArray() {
301304
return getInternalArray();
302305
}
303306

307+
public final boolean hasAttributeInMROFinalAssumptions() {
308+
return hasAttributesInMROFinalAssumptions;
309+
}
304310
}

0 commit comments

Comments
 (0)