40
40
*/
41
41
package com .oracle .graal .python .nodes .object ;
42
42
43
+ import com .oracle .graal .python .PythonLanguage ;
43
44
import com .oracle .graal .python .builtins .PythonBuiltinClassType ;
44
45
import com .oracle .graal .python .builtins .objects .object .PythonObject ;
45
- import com .oracle .graal .python .builtins .objects .type .PythonAbstractClass ;
46
46
import com .oracle .graal .python .builtins .objects .type .LazyPythonClass ;
47
+ import com .oracle .graal .python .builtins .objects .type .PythonAbstractClass ;
47
48
import com .oracle .graal .python .builtins .objects .type .PythonBuiltinClass ;
48
49
import com .oracle .graal .python .runtime .exception .PException ;
50
+ import com .oracle .truffle .api .Assumption ;
49
51
import com .oracle .truffle .api .CompilerDirectives ;
50
52
import com .oracle .truffle .api .CompilerDirectives .CompilationFinal ;
53
+ import com .oracle .truffle .api .nodes .ExplodeLoop ;
54
+ import com .oracle .truffle .api .nodes .ExplodeLoop .LoopExplosionKind ;
55
+ import com .oracle .truffle .api .object .Shape ;
51
56
52
57
public final class IsBuiltinClassProfile {
53
58
@ CompilationFinal private boolean isBuiltinType ;
@@ -57,6 +62,33 @@ public final class IsBuiltinClassProfile {
57
62
@ CompilationFinal private boolean match ;
58
63
@ CompilationFinal private boolean noMatch ;
59
64
65
+ // n.b.: (tfel) We store the python class as a Shape property on the
66
+ // DynamicObject representing the Python-level object. Thus, accessing the
67
+ // python class incurs an indirection that we'd like to avoid if
68
+ // possible. We use this cache to avoid the indirection. In the single
69
+ // context case, we just cache all classes, in the multi-context case, we
70
+ // only cache classes if they are builtin types that are shared across
71
+ // contexts.
72
+ private final Assumption singleContextAssumption = PythonLanguage .getCurrent ().singleContextAssumption ;
73
+ private static final int CLASS_CACHE_SIZE = 3 ;
74
+ @ CompilationFinal (dimensions = 1 ) private ClassCache [] classCache = new ClassCache [CLASS_CACHE_SIZE ];
75
+ @ CompilationFinal private boolean cacheUsedInSingleContext = false ;
76
+
77
+ private static final class ClassCache {
78
+ private final LazyPythonClass klass ;
79
+ private final Shape shape ;
80
+
81
+ ClassCache (Shape shape , LazyPythonClass klass ) {
82
+ this .shape = shape ;
83
+ this .klass = klass ;
84
+ }
85
+ }
86
+
87
+ private static final IsBuiltinClassProfile UNCACHED = new IsBuiltinClassProfile ();
88
+ static {
89
+ UNCACHED .classCache = null ;
90
+ }
91
+
60
92
private IsBuiltinClassProfile () {
61
93
// private constructor
62
94
}
@@ -66,27 +98,64 @@ public static IsBuiltinClassProfile create() {
66
98
}
67
99
68
100
public static IsBuiltinClassProfile getUncached () {
69
- return new IsBuiltinClassProfile () ;
101
+ return UNCACHED ;
70
102
}
71
103
72
- public boolean profileIsAnyBuiltinException (PException object ) {
73
- return profileIsAnyBuiltinClass (object .getExceptionObject ().getLazyPythonClass ());
104
+ @ ExplodeLoop (kind = LoopExplosionKind .FULL_EXPLODE_UNTIL_RETURN )
105
+ private LazyPythonClass getLazyPythonClass (PythonObject object ) {
106
+ if (classCache != null ) {
107
+ // we're still caching
108
+ if (!singleContextAssumption .isValid () && cacheUsedInSingleContext ) {
109
+ // we previously used this cache in a single context, now we're
110
+ // in a multi-context mode. Reset the cache.
111
+ CompilerDirectives .transferToInterpreterAndInvalidate ();
112
+ cacheUsedInSingleContext = false ;
113
+ classCache = new ClassCache [CLASS_CACHE_SIZE ];
114
+ }
115
+ for (int i = 0 ; i < classCache .length ; i ++) {
116
+ ClassCache cache = classCache [i ];
117
+ if (cache == null ) {
118
+ CompilerDirectives .transferToInterpreterAndInvalidate ();
119
+ Shape shape = object .getStorage ().getShape ();
120
+ LazyPythonClass klass = PythonObject .getLazyPythonClass (shape .getObjectType ());
121
+ if (klass instanceof PythonBuiltinClassType ) {
122
+ classCache [i ] = new ClassCache (shape , klass );
123
+ } else if (singleContextAssumption .isValid ()) {
124
+ // we're caching a non-builtin type, so if we switch to
125
+ // a multi-context, the cache needs to be flushed.
126
+ cacheUsedInSingleContext = true ;
127
+ classCache [i ] = new ClassCache (shape , klass );
128
+ } else {
129
+ classCache = null ;
130
+ }
131
+ return klass ;
132
+ } else if (cache .shape == object .getStorage ().getShape ()) {
133
+ return cache .klass ;
134
+ }
135
+ }
136
+ }
137
+ if (classCache != null ) {
138
+ // cache overflow, revert to generic access
139
+ CompilerDirectives .transferToInterpreterAndInvalidate ();
140
+ classCache = null ;
141
+ }
142
+ return object .getLazyPythonClass ();
74
143
}
75
144
76
145
public boolean profileIsAnyBuiltinObject (PythonObject object ) {
77
- return profileIsAnyBuiltinClass (object . getLazyPythonClass ());
146
+ return profileIsAnyBuiltinClass (getLazyPythonClass (object ));
78
147
}
79
148
80
149
public boolean profileIsOtherBuiltinObject (PythonObject object , PythonBuiltinClassType type ) {
81
- return profileIsOtherBuiltinClass (object . getLazyPythonClass (), type );
150
+ return profileIsOtherBuiltinClass (getLazyPythonClass (object ), type );
82
151
}
83
152
84
153
public boolean profileException (PException object , PythonBuiltinClassType type ) {
85
- return profileClass (object .getExceptionObject (). getLazyPythonClass ( ), type );
154
+ return profileClass (getLazyPythonClass ( object .getExceptionObject ()), type );
86
155
}
87
156
88
157
public boolean profileObject (PythonObject object , PythonBuiltinClassType type ) {
89
- return profileClass (object . getLazyPythonClass (), type );
158
+ return profileClass (getLazyPythonClass (object ), type );
90
159
91
160
}
92
161
0 commit comments