this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include "ic.h"
3
4#include "gtest/gtest.h"
5
6#include "attributedict.h"
7#include "dict-builtins.h"
8#include "str-builtins.h"
9#include "test-utils.h"
10#include "type-builtins.h"
11
12namespace py {
13namespace testing {
14
15using IcTest = RuntimeFixture;
16
17TEST_F(
18 IcTest,
19 icLookupMonomorphicWithEmptyCacheReturnsErrorNotFoundAndSetIsFoundToFalse) {
20 HandleScope scope(thread_);
21 MutableTuple caches(&scope,
22 runtime_->newMutableTuple(2 * kIcPointersPerEntry));
23 caches.fill(NoneType::object());
24 bool is_found;
25 EXPECT_TRUE(icLookupMonomorphic(*caches, 1, LayoutId::kSmallInt, &is_found)
26 .isErrorNotFound());
27 EXPECT_FALSE(is_found);
28}
29
30TEST_F(IcTest, IcLookupBinaryOpReturnsErrorNotFound) {
31 HandleScope scope(thread_);
32
33 MutableTuple caches(&scope, runtime_->newMutableTuple(kIcPointersPerEntry));
34 caches.fill(NoneType::object());
35 BinaryOpFlags flags;
36 EXPECT_TRUE(icLookupBinaryOp(*caches, 0, LayoutId::kSmallInt,
37 LayoutId::kSmallInt, &flags)
38 .isErrorNotFound());
39}
40
41TEST_F(IcTest, IcLookupGlobalVar) {
42 HandleScope scope(thread_);
43 MutableTuple caches(&scope, runtime_->newMutableTuple(2));
44 caches.fill(NoneType::object());
45 ValueCell cache(&scope, runtime_->newValueCell());
46 cache.setValue(SmallInt::fromWord(99));
47 caches.atPut(0, *cache);
48 EXPECT_TRUE(
49 isIntEqualsWord(valueCellValue(icLookupGlobalVar(*caches, 0)), 99));
50 EXPECT_TRUE(icLookupGlobalVar(*caches, 1).isNoneType());
51}
52
53TEST_F(IcTest, IcUpdateAttrSetsMonomorphicEntry) {
54 HandleScope scope(thread_);
55 MutableTuple caches(&scope,
56 runtime_->newMutableTuple(1 * kIcPointersPerEntry));
57 caches.fill(NoneType::object());
58 Object value(&scope, runtime_->newInt(88));
59 Object name(&scope, Str::empty());
60 Function dependent(&scope, newEmptyFunction());
61 EXPECT_EQ(icUpdateAttr(thread_, caches, 0, LayoutId::kSmallInt, value, name,
62 dependent),
63 ICState::kMonomorphic);
64
65 bool is_found;
66 EXPECT_EQ(icLookupMonomorphic(*caches, 0, LayoutId::kSmallInt, &is_found),
67 *value);
68}
69
70TEST_F(IcTest, IcUpdateAttrUpdatesExistingMonomorphicEntry) {
71 HandleScope scope(thread_);
72 MutableTuple caches(&scope,
73 runtime_->newMutableTuple(1 * kIcPointersPerEntry));
74 caches.fill(NoneType::object());
75 Object value(&scope, runtime_->newInt(88));
76 Object name(&scope, Str::empty());
77 Function dependent(&scope, newEmptyFunction());
78 ASSERT_EQ(icUpdateAttr(thread_, caches, 0, LayoutId::kSmallInt, value, name,
79 dependent),
80 ICState::kMonomorphic);
81 bool is_found;
82 EXPECT_EQ(icLookupMonomorphic(*caches, 0, LayoutId::kSmallInt, &is_found),
83 *value);
84 EXPECT_TRUE(is_found);
85
86 Object new_value(&scope, runtime_->newInt(99));
87 EXPECT_EQ(icUpdateAttr(thread_, caches, 0, LayoutId::kSmallInt, new_value,
88 name, dependent),
89 ICState::kMonomorphic);
90 EXPECT_EQ(icLookupMonomorphic(*caches, 0, LayoutId::kSmallInt, &is_found),
91 *new_value);
92 EXPECT_TRUE(is_found);
93}
94
95TEST_F(IcTest, IcUpdateAttrSetsPolymorphicEntry) {
96 HandleScope scope(thread_);
97 MutableTuple caches(&scope,
98 runtime_->newMutableTuple(1 * kIcPointersPerEntry));
99 caches.fill(NoneType::object());
100 Object int_value(&scope, runtime_->newInt(88));
101 Object str_value(&scope, runtime_->newInt(99));
102 Object name(&scope, Str::empty());
103 Function dependent(&scope, newEmptyFunction());
104 ASSERT_EQ(icUpdateAttr(thread_, caches, 0, LayoutId::kSmallInt, int_value,
105 name, dependent),
106 ICState::kMonomorphic);
107 EXPECT_EQ(icUpdateAttr(thread_, caches, 0, LayoutId::kSmallStr, str_value,
108 name, dependent),
109 ICState::kPolymorphic);
110 bool is_found;
111 EXPECT_EQ(icLookupPolymorphic(*caches, 0, LayoutId::kSmallInt, &is_found),
112 *int_value);
113 EXPECT_TRUE(is_found);
114 EXPECT_EQ(icLookupPolymorphic(*caches, 0, LayoutId::kSmallStr, &is_found),
115 *str_value);
116 EXPECT_TRUE(is_found);
117}
118
119TEST_F(IcTest, IcUpdateAttrUpdatesPolymorphicEntry) {
120 HandleScope scope(thread_);
121 MutableTuple caches(&scope,
122 runtime_->newMutableTuple(1 * kIcPointersPerEntry));
123 caches.fill(NoneType::object());
124 Object int_value(&scope, runtime_->newInt(88));
125 Object str_value(&scope, runtime_->newInt(99));
126 Object name(&scope, Str::empty());
127 Function dependent(&scope, newEmptyFunction());
128 ASSERT_EQ(icUpdateAttr(thread_, caches, 0, LayoutId::kSmallInt, int_value,
129 name, dependent),
130 ICState::kMonomorphic);
131 ASSERT_EQ(icUpdateAttr(thread_, caches, 0, LayoutId::kSmallStr, str_value,
132 name, dependent),
133 ICState::kPolymorphic);
134 bool is_found;
135 ASSERT_EQ(icLookupPolymorphic(*caches, 0, LayoutId::kSmallInt, &is_found),
136 *int_value);
137 ASSERT_TRUE(is_found);
138 ASSERT_EQ(icLookupPolymorphic(*caches, 0, LayoutId::kSmallStr, &is_found),
139 *str_value);
140 ASSERT_TRUE(is_found);
141
142 Object new_value(&scope, runtime_->newInt(101));
143 EXPECT_EQ(icUpdateAttr(thread_, caches, 0, LayoutId::kSmallStr, new_value,
144 name, dependent),
145 ICState::kPolymorphic);
146 EXPECT_EQ(icLookupPolymorphic(*caches, 0, LayoutId::kSmallStr, &is_found),
147 *new_value);
148 EXPECT_TRUE(is_found);
149}
150
151TEST_F(IcTest, IcUpdateAttrInsertsDependencyUpToDefiningType) {
152 HandleScope scope(thread_);
153 ASSERT_FALSE(runFromCStr(runtime_, R"(
154class A:
155 pass
156
157class B(A):
158 foo = "class B"
159
160class C(B):
161 bar = "class C"
162
163c = C()
164)")
165 .isError());
166 // Inserting dependent adds dependent to a new Placeholder in C for 'foo', and
167 // to the existing ValueCell in B. A won't be affected since it's not visited
168 // during MRO traversal.
169 MutableTuple caches(&scope, runtime_->newMutableTuple(4));
170 caches.fill(NoneType::object());
171 Object c(&scope, mainModuleAt(runtime_, "c"));
172 Object value(&scope, SmallInt::fromWord(1234));
173 Object foo(&scope, Runtime::internStrFromCStr(thread_, "foo"));
174 Function dependent(&scope, newEmptyFunction());
175 icUpdateAttr(thread_, caches, 0, c.layoutId(), value, foo, dependent);
176
177 Type type_a(&scope, mainModuleAt(runtime_, "A"));
178 RawObject unused = NoneType::object();
179 EXPECT_FALSE(attributeValueCellAt(*type_a, *foo, &unused));
180
181 Type type_b(&scope, mainModuleAt(runtime_, "B"));
182 ValueCell b_entry(&scope, typeValueCellAt(*type_b, *foo));
183 EXPECT_FALSE(b_entry.isPlaceholder());
184 WeakLink b_link(&scope, b_entry.dependencyLink());
185 EXPECT_EQ(b_link.referent(), dependent);
186 EXPECT_TRUE(b_link.next().isNoneType());
187
188 Type type_c(&scope, mainModuleAt(runtime_, "C"));
189 ValueCell c_entry(&scope, typeValueCellAt(*type_c, *foo));
190 EXPECT_TRUE(c_entry.isPlaceholder());
191 WeakLink c_link(&scope, c_entry.dependencyLink());
192 EXPECT_EQ(c_link.referent(), dependent);
193 EXPECT_TRUE(c_link.next().isNoneType());
194}
195
196TEST_F(IcTest, IcUpdateAttrDoesNotInsertsDependencyToSealedType) {
197 HandleScope scope(thread_);
198 Str instance(&scope, runtime_->newStrFromCStr("str instance"));
199 MutableTuple caches(&scope, runtime_->newMutableTuple(4));
200 caches.fill(NoneType::object());
201 Object value(&scope, SmallInt::fromWord(1234));
202 Object dunder_add(&scope, runtime_->symbols()->at(ID(__add__)));
203 Function dependent(&scope, newEmptyFunction());
204 icUpdateAttr(thread_, caches, 0, instance.layoutId(), value, dunder_add,
205 dependent);
206
207 Type type_str(&scope, runtime_->typeAt(LayoutId::kStr));
208 ValueCell dunder_add_entry(&scope, typeValueCellAt(*type_str, *dunder_add));
209 EXPECT_TRUE(dunder_add_entry.dependencyLink().isNoneType());
210}
211
212static RawObject dependencyLinkOfTypeAttr(Thread* thread, const Type& type,
213 const char* attribute_name) {
214 HandleScope scope(thread);
215 Object attribute_name_str(&scope,
216 Runtime::internStrFromCStr(thread, attribute_name));
217 ValueCell value_cell(&scope, typeValueCellAt(*type, *attribute_name_str));
218 return value_cell.dependencyLink();
219}
220
221static bool icDependentIncluded(RawObject dependent, RawObject link) {
222 for (; !link.isNoneType(); link = WeakLink::cast(link).next()) {
223 if (WeakLink::cast(link).referent() == dependent) {
224 return true;
225 }
226 }
227 return false;
228}
229
230TEST_F(IcTest, IcEvictAttr) {
231 ASSERT_FALSE(runFromCStr(runtime_, R"(
232class A:
233 def __init__(self):
234 self.foo = 4
235
236def cache_a_foo(a):
237 return a.foo
238
239a = A()
240cache_a_foo(a)
241
242class B:
243 pass
244)")
245 .isError());
246
247 HandleScope scope(thread_);
248 Type type_a(&scope, mainModuleAt(runtime_, "A"));
249 Function cache_a_foo(&scope, mainModuleAt(runtime_, "cache_a_foo"));
250 MutableTuple caches(&scope, cache_a_foo.caches());
251 Object cached_object(&scope, mainModuleAt(runtime_, "a"));
252 // Precondition check that the A.foo attribute lookup has been cached.
253 ASSERT_FALSE(
254 icLookupAttr(*caches, 1, cached_object.layoutId()).isErrorNotFound());
255 ASSERT_EQ(WeakLink::cast(dependencyLinkOfTypeAttr(thread_, type_a, "foo"))
256 .referent(),
257 *cache_a_foo);
258
259 // Try evicting caches with an attribute name that is not in the cache. This
260 // should have no effect.
261 Type cached_type(&scope, mainModuleAt(runtime_, "A"));
262 IcIterator it(&scope, runtime_, *cache_a_foo);
263 Object not_cached_attr_name(&scope,
264 Runtime::internStrFromCStr(thread_, "random"));
265 icEvictAttr(thread_, it, cached_type, not_cached_attr_name,
266 AttributeKind::kNotADataDescriptor, cache_a_foo);
267 EXPECT_FALSE(
268 icLookupAttr(*caches, 1, cached_object.layoutId()).isErrorNotFound());
269 EXPECT_EQ(WeakLink::cast(dependencyLinkOfTypeAttr(thread_, type_a, "foo"))
270 .referent(),
271 *cache_a_foo);
272
273 // Try evicting instance attribute caches for a non-data descriptor
274 // assignment. Because instance attributes have a higher priority than
275 // non-data descriptors, nothing should be evicted.
276 Object foo(&scope, Runtime::internStrFromCStr(thread_, "foo"));
277 icEvictAttr(thread_, it, cached_type, foo, AttributeKind::kNotADataDescriptor,
278 cache_a_foo);
279 EXPECT_FALSE(
280 icLookupAttr(*caches, 1, cached_object.layoutId()).isErrorNotFound());
281 EXPECT_EQ(WeakLink::cast(dependencyLinkOfTypeAttr(thread_, type_a, "foo"))
282 .referent(),
283 *cache_a_foo);
284
285 // Try evicting caches with a type that is not being cached. This should have
286 // no effect.
287 Type not_cached_type(&scope, mainModuleAt(runtime_, "B"));
288 icEvictAttr(thread_, it, not_cached_type, foo, AttributeKind::kDataDescriptor,
289 cache_a_foo);
290 EXPECT_FALSE(
291 icLookupAttr(*caches, 1, cached_object.layoutId()).isErrorNotFound());
292 EXPECT_EQ(WeakLink::cast(dependencyLinkOfTypeAttr(thread_, type_a, "foo"))
293 .referent(),
294 *cache_a_foo);
295
296 // An update to a type attribute whose type, Attribute name with a data
297 // desciptor value invalidates an instance attribute cache.
298 icEvictAttr(thread_, it, cached_type, foo, AttributeKind::kDataDescriptor,
299 cache_a_foo);
300 EXPECT_TRUE(
301 icLookupAttr(*caches, 1, cached_object.layoutId()).isErrorNotFound());
302 // The dependency for cache_a_foo gets deleted.
303 EXPECT_FALSE(icDependentIncluded(
304 *cache_a_foo, dependencyLinkOfTypeAttr(thread_, type_a, "foo")));
305}
306
307TEST_F(IcTest, IcEvictBinaryOpEvictsCacheForUpdateToLeftOperandType) {
308 ASSERT_FALSE(runFromCStr(runtime_, R"(
309class A:
310 def __ge__(self, other):
311 return True
312
313class B:
314 def __le__(self, other):
315 return True
316
317def cache_binop(a, b):
318 return a >= b
319
320a = A()
321b = B()
322
323cache_binop(a, b)
324)")
325 .isError());
326 HandleScope scope(thread_);
327 Function cache_binop(&scope, mainModuleAt(runtime_, "cache_binop"));
328 MutableTuple caches(&scope, cache_binop.caches());
329 Object left_operand(&scope, mainModuleAt(runtime_, "a"));
330 Object right_operand(&scope, mainModuleAt(runtime_, "b"));
331 Type left_operand_type(&scope, mainModuleAt(runtime_, "A"));
332 BinaryOpFlags flags_out;
333 // Precondition check that the A.__ge__ attribute lookup has been cached.
334 ASSERT_FALSE(icLookupBinaryOp(*caches, 0, left_operand.layoutId(),
335 right_operand.layoutId(), &flags_out)
336 .isErrorNotFound());
337
338 IcIterator it(&scope, runtime_, *cache_binop);
339
340 // An update to A.__ge__ invalidates the binop cache for a >= b.
341 Object dunder_ge(&scope, Runtime::internStrFromCStr(thread_, "__ge__"));
342 icEvictBinaryOp(thread_, it, left_operand_type, dunder_ge, cache_binop);
343 EXPECT_TRUE(icLookupBinaryOp(*caches, 0, left_operand.layoutId(),
344 right_operand.layoutId(), &flags_out)
345 .isErrorNotFound());
346}
347
348TEST_F(IcTest, IcEvictBinaryOpEvictsCacheForUpdateToRightOperand) {
349 ASSERT_FALSE(runFromCStr(runtime_, R"(
350class A:
351 def __ge__(self, other):
352 return True
353
354class B:
355 def __le__(self, other):
356 return True
357
358def cache_binop(a, b):
359 return a >= b
360
361a = A()
362b = B()
363
364cache_binop(a, b)
365)")
366 .isError());
367 HandleScope scope(thread_);
368 Function cache_binop(&scope, mainModuleAt(runtime_, "cache_binop"));
369 MutableTuple caches(&scope, cache_binop.caches());
370 Object left_operand(&scope, mainModuleAt(runtime_, "a"));
371 Object right_operand(&scope, mainModuleAt(runtime_, "b"));
372 Type right_operand_type(&scope, mainModuleAt(runtime_, "B"));
373 BinaryOpFlags flags_out;
374 // Precondition check that the A.__ge__ attribute lookup has been cached.
375 ASSERT_FALSE(icLookupBinaryOp(*caches, 0, left_operand.layoutId(),
376 right_operand.layoutId(), &flags_out)
377 .isErrorNotFound());
378
379 IcIterator it(&scope, runtime_, *cache_binop);
380 Object dunder_le(&scope, Runtime::internStrFromCStr(thread_, "__le__"));
381 // An update to B.__le__ invalidates the binop cache for a >= b.
382 icEvictBinaryOp(thread_, it, right_operand_type, dunder_le, cache_binop);
383 EXPECT_TRUE(icLookupBinaryOp(*caches, 0, left_operand.layoutId(),
384 right_operand.layoutId(), &flags_out)
385 .isErrorNotFound());
386}
387
388TEST_F(IcTest, IcEvictBinaryOpDoesnNotDeleteDependenciesFromCachedTypes) {
389 ASSERT_FALSE(runFromCStr(runtime_, R"(
390class A:
391 def __ge__(self, other): return True
392
393class B:
394 def __le__(self, other): return True
395
396def cache_compare_op(a, b):
397 t0 = a >= b
398 t1 = b <= 5
399
400a = A()
401b = B()
402
403cache_compare_op(a, b)
404
405A__ge__ = A.__ge__
406B__le__ = B.__le__
407)")
408 .isError());
409 HandleScope scope(thread_);
410 Object a(&scope, mainModuleAt(runtime_, "a"));
411 Object b(&scope, mainModuleAt(runtime_, "b"));
412
413 Object type_a_dunder_ge(&scope, mainModuleAt(runtime_, "A__ge__"));
414 Object type_b_dunder_le(&scope, mainModuleAt(runtime_, "B__le__"));
415 Function cache_compare_op(&scope, mainModuleAt(runtime_, "cache_compare_op"));
416 MutableTuple caches(&scope, cache_compare_op.caches());
417 BinaryOpFlags flags_out;
418 // Ensure that A.__ge__ is cached for t0 = a >= b.
419 ASSERT_EQ(
420 icLookupBinaryOp(*caches, 0, a.layoutId(), b.layoutId(), &flags_out),
421 *type_a_dunder_ge);
422 // Ensure that B.__le__ is cached for t1 = b >= 5.
423 ASSERT_EQ(icLookupBinaryOp(*caches, 1, b.layoutId(),
424 SmallInt::fromWord(0).layoutId(), &flags_out),
425 *type_b_dunder_le);
426
427 Type type_a(&scope, mainModuleAt(runtime_, "A"));
428 // Ensure cache_compare_op is a dependent of A.__ge__.
429 ASSERT_TRUE(icDependentIncluded(
430 *cache_compare_op, dependencyLinkOfTypeAttr(thread_, type_a, "__ge__")));
431
432 Type type_b(&scope, mainModuleAt(runtime_, "B"));
433 // Ensure cache_compare_op is a dependent of B.__le__.
434 ASSERT_TRUE(icDependentIncluded(
435 *cache_compare_op, dependencyLinkOfTypeAttr(thread_, type_b, "__le__")));
436
437 // Update A.__ge__ to invalidate cache for t0 = a >= b.
438 Object dunder_ge_name(&scope, Runtime::internStrFromCStr(thread_, "__ge__"));
439 icEvictCache(thread_, cache_compare_op, type_a, dunder_ge_name,
440 AttributeKind::kNotADataDescriptor);
441 // The invalidation removes dependency from cache_compare_op to A.__ge__.
442 EXPECT_FALSE(icDependentIncluded(
443 *cache_compare_op, dependencyLinkOfTypeAttr(thread_, type_a, "__ge__")));
444 // However, cache_compare_op still depends on B.__le__ since b >= 5 is cached.
445 EXPECT_TRUE(icDependentIncluded(
446 *cache_compare_op, dependencyLinkOfTypeAttr(thread_, type_b, "__le__")));
447}
448
449TEST_F(IcTest, IcDeleteDependentInValueCellDependencyLinkDeletesDependent) {
450 HandleScope scope(thread_);
451 ValueCell value_cell(&scope, runtime_->newValueCell());
452 Object dependent0(&scope, newTupleWithNone(4));
453 Object dependent1(&scope, newTupleWithNone(5));
454 Object dependent2(&scope, newTupleWithNone(6));
455 Object dependent3(&scope, newTupleWithNone(7));
456 icInsertDependentToValueCellDependencyLink(thread_, dependent3, value_cell);
457 icInsertDependentToValueCellDependencyLink(thread_, dependent2, value_cell);
458 icInsertDependentToValueCellDependencyLink(thread_, dependent1, value_cell);
459 icInsertDependentToValueCellDependencyLink(thread_, dependent0, value_cell);
460
461 // Delete the head.
462 icDeleteDependentInValueCell(thread_, value_cell, dependent0);
463
464 WeakLink link(&scope, value_cell.dependencyLink());
465 EXPECT_EQ(link.referent(), *dependent1);
466 EXPECT_TRUE(link.prev().isNoneType());
467 EXPECT_EQ(WeakLink::cast(link.next()).referent(), *dependent2);
468 EXPECT_EQ(WeakLink::cast(link.next()).prev(), *link);
469
470 // Delete the dependent in the middle.
471 icDeleteDependentInValueCell(thread_, value_cell, dependent2);
472
473 link = value_cell.dependencyLink();
474 EXPECT_EQ(link.referent(), *dependent1);
475 EXPECT_EQ(WeakLink::cast(link.next()).referent(), *dependent3);
476 EXPECT_EQ(WeakLink::cast(link.next()).prev(), *link);
477
478 // Delete the tail.
479 icDeleteDependentInValueCell(thread_, value_cell, dependent3);
480
481 link = value_cell.dependencyLink();
482 EXPECT_EQ(link.referent(), *dependent1);
483 EXPECT_TRUE(link.next().isNoneType());
484
485 // Delete the last node.
486 icDeleteDependentInValueCell(thread_, value_cell, dependent1);
487 EXPECT_TRUE(value_cell.dependencyLink().isNoneType());
488}
489
490TEST_F(
491 IcTest,
492 IcDeleteDependentFromCachedAttributeDeletesDependentUnderAttributeNameInMro) {
493 ASSERT_FALSE(runFromCStr(runtime_, R"(
494class A:
495 def foo(self): return 1
496 def bar(self): return 1
497
498def x(a):
499 return a.foo()
500
501def y(a):
502 return a.bar()
503
504a = A()
505
506x(a)
507y(a)
508)")
509 .isError());
510
511 HandleScope scope(thread_);
512 Type type_a(&scope, mainModuleAt(runtime_, "A"));
513 Object foo_name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
514 Object bar_name(&scope, Runtime::internStrFromCStr(thread_, "bar"));
515 Function dependent_x(&scope, mainModuleAt(runtime_, "x"));
516 Function dependent_y(&scope, mainModuleAt(runtime_, "y"));
517
518 // A.foo -> x
519 ValueCell foo_in_a(&scope, typeValueCellAt(*type_a, *foo_name));
520 ASSERT_EQ(WeakLink::cast(foo_in_a.dependencyLink()).referent(), *dependent_x);
521
522 // A.bar -> y
523 ValueCell bar_in_a(&scope, typeValueCellAt(*type_a, *bar_name));
524 ASSERT_EQ(WeakLink::cast(bar_in_a.dependencyLink()).referent(), *dependent_y);
525
526 LayoutId type_a_instance_layout_id = type_a.instanceLayoutId();
527 // Try to delete dependent_y under name "foo". Nothing happens.
528 icDeleteDependentFromInheritingTypes(thread_, type_a_instance_layout_id,
529 foo_name, type_a, dependent_y);
530 EXPECT_EQ(WeakLink::cast(foo_in_a.dependencyLink()).referent(), *dependent_x);
531 EXPECT_EQ(WeakLink::cast(bar_in_a.dependencyLink()).referent(), *dependent_y);
532
533 // Try to delete dependent_x under name "bar". Nothing happens.
534 icDeleteDependentFromInheritingTypes(thread_, type_a_instance_layout_id,
535 bar_name, type_a, dependent_x);
536 EXPECT_EQ(WeakLink::cast(foo_in_a.dependencyLink()).referent(), *dependent_x);
537 EXPECT_EQ(WeakLink::cast(bar_in_a.dependencyLink()).referent(), *dependent_y);
538
539 icDeleteDependentFromInheritingTypes(thread_, type_a_instance_layout_id,
540 foo_name, type_a, dependent_x);
541 EXPECT_TRUE(foo_in_a.dependencyLink().isNoneType());
542 EXPECT_EQ(WeakLink::cast(bar_in_a.dependencyLink()).referent(), *dependent_y);
543
544 icDeleteDependentFromInheritingTypes(thread_, type_a_instance_layout_id,
545 bar_name, type_a, dependent_y);
546 EXPECT_TRUE(foo_in_a.dependencyLink().isNoneType());
547 EXPECT_TRUE(bar_in_a.dependencyLink().isNoneType());
548}
549
550TEST_F(IcTest,
551 IcDeleteDependentFromCachedAttributeDeletesDependentUpToUpdatedType) {
552 ASSERT_FALSE(runFromCStr(runtime_, R"(
553class A:
554 def foo(self): return 1
555
556class B(A):
557 def foo(self): return 2
558
559class C(B):
560 pass
561
562def x(c):
563 return c.foo()
564
565c = C()
566x(c)
567
568a = A()
569x(a)
570)")
571 .isError());
572
573 HandleScope scope(thread_);
574 Type a(&scope, mainModuleAt(runtime_, "A"));
575 Type b(&scope, mainModuleAt(runtime_, "B"));
576 Type c(&scope, mainModuleAt(runtime_, "C"));
577 Object dependent_x(&scope, mainModuleAt(runtime_, "x"));
578 Object foo_name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
579
580 // A.foo -> x
581 ValueCell foo_in_a(&scope, typeValueCellAt(*a, *foo_name));
582 ASSERT_FALSE(foo_in_a.isPlaceholder());
583 ASSERT_EQ(WeakLink::cast(foo_in_a.dependencyLink()).referent(), *dependent_x);
584
585 // B.foo -> x
586 ValueCell foo_in_b(&scope, typeValueCellAt(*b, *foo_name));
587 ASSERT_FALSE(foo_in_b.isPlaceholder());
588 ASSERT_EQ(WeakLink::cast(foo_in_b.dependencyLink()).referent(), *dependent_x);
589
590 // C.foo -> x
591 // Note that this dependency is a placeholder.
592 ValueCell foo_in_c(&scope, typeValueCellAt(*c, *foo_name));
593 ASSERT_TRUE(foo_in_c.isPlaceholder());
594 ASSERT_EQ(WeakLink::cast(foo_in_c.dependencyLink()).referent(), *dependent_x);
595
596 Object c_obj(&scope, mainModuleAt(runtime_, "c"));
597 // Delete dependent_x for an update to B.foo.
598 icDeleteDependentFromInheritingTypes(thread_, c_obj.layoutId(), foo_name, b,
599 dependent_x);
600
601 // B.foo's update doesn't affect the cache for A.foo since the update does not
602 // shadow a.foo where type(a) == A.
603 EXPECT_TRUE(foo_in_c.dependencyLink().isNoneType());
604 EXPECT_TRUE(foo_in_b.dependencyLink().isNoneType());
605 // Didn't delete this since type lookup cannot reach A by successful attribute
606 // lookup for "foo" in B.
607 EXPECT_EQ(WeakLink::cast(foo_in_a.dependencyLink()).referent(), *dependent_x);
608}
609
610TEST_F(
611 IcTest,
612 IcHighestSuperTypeNotInMroOfOtherCachedTypesReturnsHighestNotCachedSuperType) {
613 ASSERT_FALSE(runFromCStr(runtime_, R"(
614class A:
615 def foo(self):
616 return 4
617
618class B(A):
619 pass
620
621def cache_foo(x):
622 return x.foo
623
624a_foo = A.foo
625b = B()
626cache_foo(b)
627)")
628 .isError());
629 HandleScope scope(thread_);
630 Function cache_foo(&scope, mainModuleAt(runtime_, "cache_foo"));
631 Object a_foo(&scope, mainModuleAt(runtime_, "a_foo"));
632 Object b_obj(&scope, mainModuleAt(runtime_, "b"));
633 Type a_type(&scope, mainModuleAt(runtime_, "A"));
634 MutableTuple caches(&scope, cache_foo.caches());
635 ASSERT_EQ(icLookupAttr(*caches, 1, b_obj.layoutId()), *a_foo);
636 // Manually delete the cache for B.foo in cache_foo.
637 caches.atPut(1 * kIcPointersPerEntry + kIcEntryKeyOffset, NoneType::object());
638 caches.atPut(1 * kIcPointersPerEntry + kIcEntryValueOffset,
639 NoneType::object());
640 ASSERT_TRUE(icLookupAttr(*caches, 1, b_obj.layoutId()).isErrorNotFound());
641
642 // Now cache_foo doesn't depend on neither A.foo nor B.foo, so this should
643 // return A.
644 Object foo(&scope, Runtime::internStrFromCStr(thread_, "foo"));
645 Object result(&scope, icHighestSuperTypeNotInMroOfOtherCachedTypes(
646 thread_, b_obj.layoutId(), foo, cache_foo));
647 EXPECT_EQ(result, *a_type);
648}
649
650TEST_F(IcTest, IcIsCachedAttributeAffectedByUpdatedType) {
651 ASSERT_FALSE(runFromCStr(runtime_, R"(
652class A:
653 def foo(self): return 1
654
655class B(A):
656 def foo(self): return 2
657
658class C(B):
659 pass
660
661
662def x(c):
663 return c.foo()
664
665c = C()
666x(c)
667)")
668 .isError());
669 HandleScope scope(thread_);
670 Type type_a(&scope, mainModuleAt(runtime_, "A"));
671 Type type_b(&scope, mainModuleAt(runtime_, "B"));
672 Type type_c(&scope, mainModuleAt(runtime_, "C"));
673 Object foo_name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
674
675 LayoutId type_c_instance_layout_id = type_c.instanceLayoutId();
676 // Check if A.foo is not retrived from C.foo.
677 EXPECT_FALSE(icIsCachedAttributeAffectedByUpdatedType(
678 thread_, type_c_instance_layout_id, foo_name, type_a));
679 // Check if B.foo is retrieved from C.foo.
680 EXPECT_TRUE(icIsCachedAttributeAffectedByUpdatedType(
681 thread_, type_c_instance_layout_id, foo_name, type_b));
682
683 // Assign C.foo to a real value.
684 ValueCell foo_in_c(&scope, typeValueCellAt(*type_c, *foo_name));
685 foo_in_c.setValue(NoneType::object());
686 // Check if B.foo is not retrived from C.foo from now on.
687 EXPECT_FALSE(icIsCachedAttributeAffectedByUpdatedType(
688 thread_, type_c_instance_layout_id, foo_name, type_b));
689 // Instead, C.foo is retrieved.
690 EXPECT_TRUE(icIsCachedAttributeAffectedByUpdatedType(
691 thread_, type_c_instance_layout_id, foo_name, type_c));
692}
693
694// Create a function that maps cache index 1 to the given attribute name.
695static RawObject testingFunctionCachingAttributes(
696 Thread* thread, const Object& attribute_name) {
697 Runtime* runtime = thread->runtime();
698 HandleScope scope(thread);
699 Tuple consts(&scope, runtime->emptyTuple());
700 Object name(&scope, Str::empty());
701 Tuple names(&scope, runtime->newTupleWith2(attribute_name, name));
702 Code code(&scope,
703 newCodeWithBytesConstsNames(View<byte>(nullptr, 0), consts, names));
704
705 MutableBytes rewritten_bytecode(&scope,
706 runtime->newMutableBytesUninitialized(8));
707 rewrittenBytecodeOpAtPut(rewritten_bytecode, 0, LOAD_ATTR_ANAMORPHIC);
708 rewrittenBytecodeArgAtPut(rewritten_bytecode, 0, 0);
709 rewrittenBytecodeCacheAtPut(rewritten_bytecode, 0, 1);
710
711 Module module(&scope, findMainModule(runtime));
712 Function function(&scope,
713 runtime->newFunctionWithCode(thread, name, code, module));
714 function.setRewrittenBytecode(*rewritten_bytecode);
715
716 MutableTuple caches(&scope,
717 runtime->newMutableTuple(2 * kIcPointersPerEntry));
718 caches.fill(NoneType::object());
719 function.setCaches(*caches);
720
721 return *function;
722}
723
724TEST_F(IcTest, IcEvictCacheEvictsCachesForMatchingAttributeName) {
725 ASSERT_FALSE(runFromCStr(runtime_, R"(
726class C: pass
727
728c = C()
729)")
730 .isError());
731 HandleScope scope(thread_);
732 Type type(&scope, mainModuleAt(runtime_, "C"));
733 Object foo_name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
734 Object bar_name(&scope, Runtime::internStrFromCStr(thread_, "bar"));
735 Function dependent(&scope,
736 testingFunctionCachingAttributes(thread_, foo_name));
737
738 // foo -> dependent.
739 ValueCell foo(&scope, attributeValueCellAtPut(thread_, type, foo_name));
740 ASSERT_TRUE(
741 icInsertDependentToValueCellDependencyLink(thread_, dependent, foo));
742
743 // Create an attribute cache for an instance of C, under name "foo".
744 Object instance(&scope, mainModuleAt(runtime_, "c"));
745 MutableTuple caches(&scope, dependent.caches());
746 Object value(&scope, SmallInt::fromWord(1234));
747 Object name(&scope, Str::empty());
748 icUpdateAttr(thread_, caches, 1, instance.layoutId(), value, name, dependent);
749 ASSERT_EQ(icLookupAttr(*caches, 1, instance.layoutId()),
750 SmallInt::fromWord(1234));
751
752 // Deleting caches for "bar" doesn't affect the cache for "foo".
753 icEvictCache(thread_, dependent, type, bar_name,
754 AttributeKind::kDataDescriptor);
755 EXPECT_EQ(icLookupAttr(*caches, 1, instance.layoutId()),
756 SmallInt::fromWord(1234));
757
758 // Deleting caches for "foo".
759 icEvictCache(thread_, dependent, type, foo_name,
760 AttributeKind::kDataDescriptor);
761 EXPECT_TRUE(icLookupAttr(*caches, 1, instance.layoutId()).isErrorNotFound());
762}
763
764TEST_F(IcTest,
765 IcEvictCacheEvictsCachesForInstanceOffsetOnlyWhenDataDesciptorIsTrue) {
766 ASSERT_FALSE(runFromCStr(runtime_, R"(
767class C: pass
768
769c = C()
770)")
771 .isError());
772 HandleScope scope(thread_);
773 Type type(&scope, mainModuleAt(runtime_, "C"));
774 Object foo_name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
775 Function dependent(&scope,
776 testingFunctionCachingAttributes(thread_, foo_name));
777
778 // foo -> dependent.
779 ValueCell foo(&scope, attributeValueCellAtPut(thread_, type, foo_name));
780 ASSERT_TRUE(
781 icInsertDependentToValueCellDependencyLink(thread_, dependent, foo));
782
783 // Create an instance offset cache for an instance of C, under name "foo".
784 Object instance(&scope, mainModuleAt(runtime_, "c"));
785 MutableTuple caches(&scope, dependent.caches());
786 Object value(&scope, SmallInt::fromWord(1234));
787 Object name(&scope, Str::empty());
788 icUpdateAttr(thread_, caches, 1, instance.layoutId(), value, name, dependent);
789 ASSERT_EQ(icLookupAttr(*caches, 1, instance.layoutId()),
790 SmallInt::fromWord(1234));
791
792 // An attempt to delete caches for "foo" with data_descriptor == false doesn't
793 // affect it.
794 icEvictCache(thread_, dependent, type, foo_name,
795 AttributeKind::kNotADataDescriptor);
796 EXPECT_EQ(icLookupAttr(*caches, 1, instance.layoutId()),
797 SmallInt::fromWord(1234));
798
799 // Delete caches for "foo" with data_descriptor == true actually deletes it.
800 icEvictCache(thread_, dependent, type, foo_name,
801 AttributeKind::kDataDescriptor);
802 EXPECT_TRUE(icLookupAttr(*caches, 1, instance.layoutId()).isErrorNotFound());
803}
804
805TEST_F(IcTest, IcEvictCacheEvictsOnlyAffectedCaches) {
806 ASSERT_FALSE(runFromCStr(runtime_, R"(
807class A:
808 def foo(self): return 1
809
810class B(A):
811 def foo(self): return 2
812
813class C(B): pass
814
815a = A()
816b = B()
817c = C()
818)")
819 .isError());
820 HandleScope scope(thread_);
821 Type a_type(&scope, mainModuleAt(runtime_, "A"));
822 Type b_type(&scope, mainModuleAt(runtime_, "B"));
823 Type c_type(&scope, mainModuleAt(runtime_, "C"));
824 Object foo_name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
825 Function dependent(&scope,
826 testingFunctionCachingAttributes(thread_, foo_name));
827
828 // The following lines simulate that dependent caches a.foo, b.foo, c.foo, and
829 // x.foo. A.foo -> dependent.
830 ValueCell a_foo(&scope, typeValueCellAt(*a_type, *foo_name));
831 ASSERT_TRUE(
832 icInsertDependentToValueCellDependencyLink(thread_, dependent, a_foo));
833 // B.foo -> dependent.
834 ValueCell b_foo(&scope, typeValueCellAt(*b_type, *foo_name));
835 ASSERT_TRUE(
836 icInsertDependentToValueCellDependencyLink(thread_, dependent, b_foo));
837 // C.foo -> dependent.
838 ValueCell c_foo(&scope, attributeValueCellAtPut(thread_, c_type, foo_name));
839 // This is a placeholder since C.foo is resolved to B.foo.
840 c_foo.makePlaceholder();
841 ASSERT_TRUE(
842 icInsertDependentToValueCellDependencyLink(thread_, dependent, c_foo));
843
844 // Create a cache for a.foo in dependent.
845 Object a(&scope, mainModuleAt(runtime_, "a"));
846 MutableTuple caches(&scope, dependent.caches());
847 Object value_100(&scope, SmallInt::fromWord(100));
848 Object name(&scope, Str::empty());
849 icUpdateAttr(thread_, caches, 1, a.layoutId(), value_100, name, dependent);
850 ASSERT_EQ(icLookupAttr(*caches, 1, a.layoutId()), SmallInt::fromWord(100));
851 // Create a cache for b.foo in dependent.
852 Object b(&scope, mainModuleAt(runtime_, "b"));
853 Object value_200(&scope, SmallInt::fromWord(200));
854 icUpdateAttr(thread_, caches, 1, b.layoutId(), value_200, name, dependent);
855 ASSERT_EQ(icLookupAttr(*caches, 1, b.layoutId()), SmallInt::fromWord(200));
856 // Create a cache for c.foo in dependent.
857 Object c(&scope, mainModuleAt(runtime_, "c"));
858 Object value_300(&scope, SmallInt::fromWord(300));
859 icUpdateAttr(thread_, caches, 1, c.layoutId(), value_300, name, dependent);
860 ASSERT_EQ(icLookupAttr(*caches, 1, c.layoutId()), SmallInt::fromWord(300));
861
862 // Trigger invalidation by updating B.foo.
863 icEvictCache(thread_, dependent, b_type, foo_name,
864 AttributeKind::kDataDescriptor);
865 // Note that only caches made for the type attribute are evincted, and
866 // dependent is dropped from them.
867 EXPECT_EQ(icLookupAttr(*caches, 1, a.layoutId()), SmallInt::fromWord(100));
868 EXPECT_EQ(WeakLink::cast(a_foo.dependencyLink()).referent(), *dependent);
869 EXPECT_TRUE(icLookupAttr(*caches, 1, b.layoutId()).isErrorNotFound());
870 EXPECT_TRUE(b_foo.dependencyLink().isNoneType());
871 EXPECT_TRUE(icLookupAttr(*caches, 1, c.layoutId()).isErrorNotFound());
872 EXPECT_TRUE(c_foo.dependencyLink().isNoneType());
873
874 // Trigger invalidation by updating A.foo.
875 icEvictCache(thread_, dependent, a_type, foo_name,
876 AttributeKind::kDataDescriptor);
877 EXPECT_TRUE(icLookupAttr(*caches, 1, a.layoutId()).isErrorNotFound());
878 EXPECT_TRUE(a_foo.dependencyLink().isNoneType());
879}
880
881TEST_F(IcTest, IcEvictCacheWithPolymorphicCacheEvictsCache) {
882 ASSERT_FALSE(runFromCStr(runtime_, R"(
883class A: pass
884
885class B: pass
886
887a = A()
888b = B()
889)")
890 .isError());
891 HandleScope scope(thread_);
892 Type a_type(&scope, mainModuleAt(runtime_, "A"));
893 Object a(&scope, mainModuleAt(runtime_, "a"));
894 Type b_type(&scope, mainModuleAt(runtime_, "B"));
895 Object b(&scope, mainModuleAt(runtime_, "b"));
896
897 Object a_value(&scope, runtime_->newInt(88));
898 Object b_value(&scope, runtime_->newInt(99));
899 Object name(&scope, Str::empty());
900 Function dependent(&scope, testingFunctionCachingAttributes(thread_, name));
901 MutableTuple caches(&scope, dependent.caches());
902 ASSERT_EQ(
903 icUpdateAttr(thread_, caches, 1, a.layoutId(), a_value, name, dependent),
904 ICState::kMonomorphic);
905 EXPECT_EQ(
906 icUpdateAttr(thread_, caches, 1, b.layoutId(), b_value, name, dependent),
907 ICState::kPolymorphic);
908 bool is_found;
909 EXPECT_EQ(icLookupPolymorphic(*caches, 1, a.layoutId(), &is_found), *a_value);
910 EXPECT_TRUE(is_found);
911 EXPECT_EQ(icLookupPolymorphic(*caches, 1, b.layoutId(), &is_found), *b_value);
912 EXPECT_TRUE(is_found);
913 icEvictCache(thread_, dependent, a_type, name,
914 AttributeKind::kDataDescriptor);
915 EXPECT_TRUE(icLookupPolymorphic(*caches, 1, a.layoutId(), &is_found)
916 .isErrorNotFound());
917 EXPECT_FALSE(is_found);
918 icEvictCache(thread_, dependent, b_type, name,
919 AttributeKind::kDataDescriptor);
920 EXPECT_TRUE(icLookupPolymorphic(*caches, 1, b.layoutId(), &is_found)
921 .isErrorNotFound());
922 EXPECT_FALSE(is_found);
923}
924
925// Verify if IcInvalidateCachesForTypeAttr calls
926// DeleteCachesForTypeAttrInDependent with all dependents.
927TEST_F(IcTest, IcInvalidateCachesForTypeAttrProcessesAllDependents) {
928 ASSERT_FALSE(runFromCStr(runtime_, R"(
929class C: pass
930
931c = C()
932)")
933 .isError());
934 HandleScope scope(thread_);
935 Type type(&scope, mainModuleAt(runtime_, "C"));
936 Object foo_name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
937 Object bar_name(&scope, Runtime::internStrFromCStr(thread_, "bar"));
938 Function dependent0(&scope,
939 testingFunctionCachingAttributes(thread_, foo_name));
940 Function dependent1(&scope,
941 testingFunctionCachingAttributes(thread_, bar_name));
942
943 // Create a property so these value cells look like data descriptor attributes
944 Object none(&scope, NoneType::object());
945 Object data_descriptor(&scope, runtime_->newProperty(none, none, none));
946
947 // foo -> dependent0.
948 ValueCell foo(&scope, attributeValueCellAtPut(thread_, type, foo_name));
949 foo.setValue(*data_descriptor);
950 ASSERT_TRUE(
951 icInsertDependentToValueCellDependencyLink(thread_, dependent0, foo));
952
953 // bar -> dependent1.
954 ValueCell bar(&scope, attributeValueCellAtPut(thread_, type, bar_name));
955 bar.setValue(*data_descriptor);
956
957 ASSERT_TRUE(
958 icInsertDependentToValueCellDependencyLink(thread_, dependent1, bar));
959
960 MutableTuple dependent0_caches(&scope, dependent0.caches());
961 Object instance(&scope, mainModuleAt(runtime_, "c"));
962 {
963 // Create an attribute cache for an instance of C, under name "foo" in
964 // dependent0.
965 Object name(&scope, Str::empty());
966 Object value(&scope, SmallInt::fromWord(1234));
967 icUpdateAttr(thread_, dependent0_caches, 1, instance.layoutId(), value,
968 name, dependent0);
969 ASSERT_EQ(icLookupAttr(*dependent0_caches, 1, instance.layoutId()),
970 SmallInt::fromWord(1234));
971 }
972
973 MutableTuple dependent1_caches(&scope, dependent1.caches());
974 {
975 // Create an attribute cache for an instance of C, under name "bar" in
976 // dependent1.
977 Object name(&scope, Str::empty());
978 Object value(&scope, SmallInt::fromWord(5678));
979 icUpdateAttr(thread_, dependent1_caches, 1, instance.layoutId(), value,
980 name, dependent1);
981 ASSERT_EQ(icLookupAttr(*dependent1_caches, 1, instance.layoutId()),
982 SmallInt::fromWord(5678));
983 }
984
985 icInvalidateAttr(thread_, type, foo_name, foo);
986 EXPECT_TRUE(icLookupAttr(*dependent0_caches, 1, instance.layoutId())
987 .isErrorNotFound());
988 EXPECT_EQ(icLookupAttr(*dependent1_caches, 1, instance.layoutId()),
989 SmallInt::fromWord(5678));
990
991 icInvalidateAttr(thread_, type, bar_name, bar);
992 EXPECT_TRUE(icLookupAttr(*dependent0_caches, 1, instance.layoutId())
993 .isErrorNotFound());
994 EXPECT_TRUE(icLookupAttr(*dependent1_caches, 1, instance.layoutId())
995 .isErrorNotFound());
996}
997
998TEST_F(IcTest,
999 BinarySubscrUpdateCacheWithRaisingDescriptorPropagatesException) {
1000 EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, R"(
1001class Desc:
1002 def __get__(self, instance, type):
1003 raise UserWarning("foo")
1004
1005class C:
1006 __getitem__ = Desc()
1007
1008container = C()
1009result = container[0]
1010)"),
1011 LayoutId::kUserWarning, "foo"));
1012}
1013
1014TEST_F(IcTest, IcIsAttrCachedInDependentReturnsTrueForAttrCaches) {
1015 ASSERT_FALSE(runFromCStr(runtime_, R"(
1016class X:
1017 def foo(self): return 4
1018
1019class Y(X):
1020 pass
1021
1022class A:
1023 def foo(self): return 4
1024
1025class B(A):
1026 pass
1027
1028def cache_Y_foo():
1029 return Y().foo()
1030
1031cache_Y_foo()
1032)")
1033 .isError());
1034 HandleScope scope(thread_);
1035 Type type_a(&scope, mainModuleAt(runtime_, "A"));
1036 Type type_b(&scope, mainModuleAt(runtime_, "B"));
1037 Type type_x(&scope, mainModuleAt(runtime_, "X"));
1038 Type type_y(&scope, mainModuleAt(runtime_, "Y"));
1039 Object foo(&scope, Runtime::internStrFromCStr(thread_, "foo"));
1040 Object bar(&scope, Runtime::internStrFromCStr(thread_, "bar"));
1041 Function cache_y_foo(&scope, mainModuleAt(runtime_, "cache_Y_foo"));
1042
1043 // Note that cache_y_foo depends both on X.foo and Y.foo since an
1044 // update to either one of them flows to Y().foo().
1045 EXPECT_TRUE(icIsAttrCachedInDependent(thread_, type_x, foo, cache_y_foo));
1046 EXPECT_TRUE(icIsAttrCachedInDependent(thread_, type_y, foo, cache_y_foo));
1047 EXPECT_FALSE(icIsAttrCachedInDependent(thread_, type_x, bar, cache_y_foo));
1048 EXPECT_FALSE(icIsAttrCachedInDependent(thread_, type_a, foo, cache_y_foo));
1049 EXPECT_FALSE(icIsAttrCachedInDependent(thread_, type_b, foo, cache_y_foo));
1050}
1051
1052TEST_F(IcTest, IcIsAttrCachedInDependentReturnsTrueForBinaryOpCaches) {
1053 ASSERT_FALSE(runFromCStr(runtime_, R"(
1054class X:
1055 def __ge__(self, other): return 5
1056
1057class Y(X):
1058 pass
1059
1060class A:
1061 def foo(self): return 4
1062
1063class B(A):
1064 pass
1065
1066def cache_Y_ge():
1067 return Y() >= B()
1068
1069cache_Y_ge()
1070)")
1071 .isError());
1072 HandleScope scope(thread_);
1073 Type type_x(&scope, mainModuleAt(runtime_, "X"));
1074 Type type_y(&scope, mainModuleAt(runtime_, "Y"));
1075 Type type_a(&scope, mainModuleAt(runtime_, "A"));
1076 Type type_b(&scope, mainModuleAt(runtime_, "B"));
1077 Object dunder_ge(&scope, Runtime::internStrFromCStr(thread_, "__ge__"));
1078 Object dunder_le(&scope, Runtime::internStrFromCStr(thread_, "__le__"));
1079 Function cache_ge(&scope, mainModuleAt(runtime_, "cache_Y_ge"));
1080
1081 // Note that cache_ge indirectly depends on X, but directly on Y since both
1082 // X.__ge__ and Y.__ge__ affect Y() >= sth.
1083 EXPECT_TRUE(icIsAttrCachedInDependent(thread_, type_x, dunder_ge, cache_ge));
1084 EXPECT_TRUE(icIsAttrCachedInDependent(thread_, type_y, dunder_ge, cache_ge));
1085 // Note that cache_ge indirectly depends on A, but directly on B since both
1086 // B.__le__ and C.__le__ affect sth >= B().
1087 EXPECT_TRUE(icIsAttrCachedInDependent(thread_, type_a, dunder_le, cache_ge));
1088 EXPECT_TRUE(icIsAttrCachedInDependent(thread_, type_b, dunder_le, cache_ge));
1089
1090 EXPECT_FALSE(icIsAttrCachedInDependent(thread_, type_x, dunder_le, cache_ge));
1091 EXPECT_FALSE(icIsAttrCachedInDependent(thread_, type_y, dunder_le, cache_ge));
1092 EXPECT_FALSE(icIsAttrCachedInDependent(thread_, type_a, dunder_ge, cache_ge));
1093 EXPECT_FALSE(icIsAttrCachedInDependent(thread_, type_b, dunder_ge, cache_ge));
1094}
1095
1096TEST_F(IcTest, IcDependentIncludedWithNoneLinkReturnsFalse) {
1097 EXPECT_FALSE(icDependentIncluded(Unbound::object(), NoneType::object()));
1098}
1099
1100TEST_F(IcTest, IcDependentIncludedWithDependentInChainReturnsTrue) {
1101 HandleScope scope(thread_);
1102 Object none(&scope, NoneType::object());
1103 Object one(&scope, runtime_->newSet());
1104 Object two(&scope, runtime_->newSet());
1105 Object three(&scope, runtime_->newSet());
1106 // Set up None <- link0 <-> link1 <-> link2 -> None
1107 WeakLink link0(&scope, runtime_->newWeakLink(thread_, one, none, none));
1108 WeakLink link1(&scope, runtime_->newWeakLink(thread_, two, link0, none));
1109 WeakLink link2(&scope, runtime_->newWeakLink(thread_, three, link1, none));
1110 link0.setNext(*link1);
1111 link1.setNext(*link2);
1112 EXPECT_TRUE(icDependentIncluded(*one, *link0));
1113 EXPECT_TRUE(icDependentIncluded(*two, *link0));
1114 EXPECT_TRUE(icDependentIncluded(*three, *link0));
1115
1116 EXPECT_FALSE(icDependentIncluded(*one, *link1));
1117 EXPECT_TRUE(icDependentIncluded(*two, *link1));
1118 EXPECT_TRUE(icDependentIncluded(*three, *link1));
1119
1120 EXPECT_FALSE(icDependentIncluded(*one, *link2));
1121 EXPECT_FALSE(icDependentIncluded(*two, *link2));
1122 EXPECT_TRUE(icDependentIncluded(*three, *link2));
1123
1124 EXPECT_FALSE(icDependentIncluded(Unbound::object(), *link0));
1125 EXPECT_FALSE(icDependentIncluded(Unbound::object(), *link1));
1126 EXPECT_FALSE(icDependentIncluded(Unbound::object(), *link2));
1127}
1128
1129TEST_F(IcTest, IcEvictCacheEvictsCompareOpCaches) {
1130 ASSERT_FALSE(runFromCStr(runtime_, R"(
1131class A:
1132 def __ge__(self, other): return True
1133
1134class B: pass
1135
1136def cache_compare_op(a, b):
1137 return a >= b
1138
1139a = A()
1140b = B()
1141A__ge__ = A.__ge__
1142
1143cache_compare_op(a, b)
1144)")
1145 .isError());
1146 HandleScope scope(thread_);
1147 Object a(&scope, mainModuleAt(runtime_, "a"));
1148 Object b(&scope, mainModuleAt(runtime_, "b"));
1149 Object type_a_dunder_ge(&scope, mainModuleAt(runtime_, "A__ge__"));
1150 Function cache_compare_op(&scope, mainModuleAt(runtime_, "cache_compare_op"));
1151 MutableTuple caches(&scope, cache_compare_op.caches());
1152 BinaryOpFlags flags_out;
1153 Object cached(&scope, icLookupBinaryOp(*caches, 0, a.layoutId(), b.layoutId(),
1154 &flags_out));
1155 // Precondition check that the A.__ge__ lookup has been cached.
1156 ASSERT_EQ(*cached, *type_a_dunder_ge);
1157 Type type_a(&scope, mainModuleAt(runtime_, "A"));
1158 Object dunder_ge_name(&scope, Runtime::internStrFromCStr(thread_, "__ge__"));
1159 ValueCell dunder_ge(&scope, typeValueCellAt(*type_a, *dunder_ge_name));
1160 WeakLink dunder_ge_link(&scope, dunder_ge.dependencyLink());
1161 // Precondition check that cache_compare_op is a dependent of A.__ge__.
1162 ASSERT_EQ(dunder_ge_link.referent(), *cache_compare_op);
1163 Type type_b(&scope, mainModuleAt(runtime_, "B"));
1164 Object dunder_le_name(&scope, Runtime::internStrFromCStr(thread_, "__le__"));
1165 ValueCell dunder_le(&scope, typeValueCellAt(*type_b, *dunder_le_name));
1166 WeakLink dunder_le_link(&scope, dunder_le.dependencyLink());
1167 // Precondition check that cache_compare_op is a dependent of B.__le__.
1168 ASSERT_EQ(dunder_le_link.referent(), *cache_compare_op);
1169
1170 // Updating A.__ge__ triggers cache invalidation.
1171 icEvictCache(thread_, cache_compare_op, type_a, dunder_ge_name,
1172 AttributeKind::kNotADataDescriptor);
1173 EXPECT_TRUE(
1174 icLookupBinaryOp(*caches, 0, a.layoutId(), b.layoutId(), &flags_out)
1175 .isErrorNotFound());
1176 EXPECT_FALSE(
1177 icDependentIncluded(*cache_compare_op, dunder_ge.dependencyLink()));
1178 EXPECT_TRUE(dunder_le.dependencyLink().isNoneType());
1179}
1180
1181TEST_F(IcTest,
1182 ForIterUpdateCacheWithRaisingDescriptorDunderNextPropagatesException) {
1183 EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, R"(
1184class Desc:
1185 def __get__(self, instance, type):
1186 raise UserWarning("foo")
1187
1188class C:
1189 def __iter__(self):
1190 return self
1191 __next__ = Desc()
1192
1193container = C()
1194result = [x for x in container]
1195)"),
1196 LayoutId::kUserWarning, "foo"));
1197}
1198
1199TEST_F(IcTest, BinarySubscrUpdateCacheWithFunctionUpdatesCache) {
1200 ASSERT_FALSE(runFromCStr(runtime_, R"(
1201class Container:
1202 def __getitem__(self, index):
1203 return index + 1
1204
1205def f(c, k):
1206 return c[k]
1207
1208container = Container()
1209getitem = type(container).__getitem__
1210result = f(container, 0)
1211)")
1212 .isError());
1213
1214 HandleScope scope(thread_);
1215 Object result(&scope, mainModuleAt(runtime_, "result"));
1216 EXPECT_TRUE(isIntEqualsWord(*result, 1));
1217
1218 Object container(&scope, mainModuleAt(runtime_, "container"));
1219 Object getitem(&scope, mainModuleAt(runtime_, "getitem"));
1220 Function f(&scope, mainModuleAt(runtime_, "f"));
1221 MutableTuple caches(&scope, f.caches());
1222 // Expect that BINARY_SUBSCR is the only cached opcode in f().
1223 ASSERT_EQ(caches.length(), 1 * kIcPointersPerEntry);
1224 EXPECT_EQ(icLookupAttr(*caches, 0, container.layoutId()), *getitem);
1225
1226 ASSERT_FALSE(runFromCStr(runtime_, R"(
1227container2 = Container()
1228result2 = f(container2, 1)
1229)")
1230 .isError());
1231 Object container2(&scope, mainModuleAt(runtime_, "container2"));
1232 Object result2(&scope, mainModuleAt(runtime_, "result2"));
1233 EXPECT_EQ(container2.layoutId(), container.layoutId());
1234 EXPECT_TRUE(isIntEqualsWord(*result2, 2));
1235}
1236
1237TEST_F(IcTest, BinarySubscrUpdateCacheWithNonFunctionDoesntUpdateCache) {
1238 ASSERT_FALSE(runFromCStr(runtime_, R"(
1239def f(c, k):
1240 return c[k]
1241class Container:
1242 def get(self):
1243 def getitem(key):
1244 return key
1245 return getitem
1246
1247 __getitem__ = property(get)
1248
1249container = Container()
1250result = f(container, "hi")
1251)")
1252 .isError());
1253
1254 HandleScope scope(thread_);
1255 Object result(&scope, mainModuleAt(runtime_, "result"));
1256 EXPECT_TRUE(isStrEqualsCStr(*result, "hi"));
1257
1258 Object container(&scope, mainModuleAt(runtime_, "container"));
1259 Function f(&scope, mainModuleAt(runtime_, "f"));
1260 MutableTuple caches(&scope, f.caches());
1261 // Expect that BINARY_SUBSCR is the only cached opcode in f().
1262 ASSERT_EQ(caches.length(), 1 * kIcPointersPerEntry);
1263 EXPECT_TRUE(icLookupAttr(*caches, 0, container.layoutId()).isErrorNotFound());
1264
1265 ASSERT_FALSE(runFromCStr(runtime_, R"(
1266container2 = Container()
1267result2 = f(container, "hello there!")
1268)")
1269 .isError());
1270 Object container2(&scope, mainModuleAt(runtime_, "container2"));
1271 Object result2(&scope, mainModuleAt(runtime_, "result2"));
1272 ASSERT_EQ(container2.layoutId(), container.layoutId());
1273 EXPECT_TRUE(isStrEqualsCStr(*result2, "hello there!"));
1274}
1275
1276TEST_F(IcTest, IcUpdateBinaryOpSetsEmptyEntry) {
1277 HandleScope scope(thread_);
1278 MutableTuple caches(&scope,
1279 runtime_->newMutableTuple(2 * kIcPointersPerEntry));
1280 caches.fill(NoneType::object());
1281 Object value(&scope, runtime_->newStrFromCStr("this is a random value"));
1282 EXPECT_EQ(icUpdateBinOp(thread_, caches, 1, LayoutId::kLargeInt,
1283 LayoutId::kSmallInt, value, kBinaryOpNone),
1284 ICState::kMonomorphic);
1285 BinaryOpFlags flags;
1286 EXPECT_EQ(icLookupBinOpMonomorphic(*caches, 1, LayoutId::kLargeInt,
1287 LayoutId::kSmallInt, &flags),
1288 *value);
1289}
1290
1291TEST_F(IcTest, IcUpdateBinaryOpSetsExistingMonomorphicEntry) {
1292 HandleScope scope(thread_);
1293
1294 MutableTuple caches(&scope,
1295 runtime_->newMutableTuple(2 * kIcPointersPerEntry));
1296 caches.fill(NoneType::object());
1297 Object value(&scope, runtime_->newStrFromCStr("xxx"));
1298 ASSERT_EQ(icUpdateBinOp(thread_, caches, 1, LayoutId::kLargeInt,
1299 LayoutId::kSmallInt, value, kBinaryOpNone),
1300 ICState::kMonomorphic);
1301 Object new_value(&scope, runtime_->newStrFromCStr("yyy"));
1302 EXPECT_EQ(icUpdateBinOp(thread_, caches, 1, LayoutId::kLargeInt,
1303 LayoutId::kSmallInt, new_value, kBinaryOpNone),
1304 ICState::kMonomorphic);
1305 BinaryOpFlags flags;
1306 EXPECT_EQ(icLookupBinOpMonomorphic(*caches, 1, LayoutId::kLargeInt,
1307 LayoutId::kSmallInt, &flags),
1308 *new_value);
1309}
1310
1311TEST_F(IcTest, IcUpdateBinaryOpSetsExistingPolymorphicEntry) {
1312 HandleScope scope(thread_);
1313
1314 MutableTuple caches(&scope,
1315 runtime_->newMutableTuple(2 * kIcPointersPerEntry));
1316 caches.fill(NoneType::object());
1317 Object value(&scope, runtime_->newStrFromCStr("xxx"));
1318 ASSERT_EQ(icUpdateBinOp(thread_, caches, 1, LayoutId::kLargeInt,
1319 LayoutId::kSmallInt, value, kBinaryOpNone),
1320 ICState::kMonomorphic);
1321 BinaryOpFlags flags;
1322 ASSERT_EQ(icLookupBinOpMonomorphic(*caches, 1, LayoutId::kLargeInt,
1323 LayoutId::kSmallInt, &flags),
1324 *value);
1325
1326 ASSERT_EQ(icUpdateBinOp(thread_, caches, 1, LayoutId::kSmallInt,
1327 LayoutId::kLargeInt, value, kBinaryOpNone),
1328 ICState::kPolymorphic);
1329 ASSERT_EQ(icLookupBinOpPolymorphic(*caches, 1, LayoutId::kSmallInt,
1330 LayoutId::kLargeInt, &flags),
1331 *value);
1332
1333 Object new_value(&scope, runtime_->newStrFromCStr("yyy"));
1334 EXPECT_EQ(icUpdateBinOp(thread_, caches, 1, LayoutId::kLargeInt,
1335 LayoutId::kSmallInt, new_value, kBinaryOpNone),
1336 ICState::kPolymorphic);
1337 EXPECT_EQ(icLookupBinOpPolymorphic(*caches, 1, LayoutId::kLargeInt,
1338 LayoutId::kSmallInt, &flags),
1339 *new_value);
1340}
1341
1342TEST_F(IcTest, ForIterUpdateCacheWithFunctionUpdatesCache) {
1343 ASSERT_FALSE(runFromCStr(runtime_, R"(
1344def f(container):
1345 for i in container:
1346 return i
1347
1348class C:
1349 def __iter__(self):
1350 return Iterator()
1351
1352class Iterator:
1353 def __init__(self):
1354 self.next_called = False
1355
1356 def __next__(self):
1357 if self.next_called:
1358 raise StopIteration
1359 return 1
1360
1361container = C()
1362iterator = iter(container)
1363iter_next = Iterator.__next__
1364result = f(container)
1365)")
1366 .isError());
1367
1368 HandleScope scope(thread_);
1369 Object result(&scope, mainModuleAt(runtime_, "result"));
1370 EXPECT_TRUE(isIntEqualsWord(*result, 1));
1371
1372 Object iterator(&scope, mainModuleAt(runtime_, "iterator"));
1373 Object iter_next(&scope, mainModuleAt(runtime_, "iter_next"));
1374 Function f(&scope, mainModuleAt(runtime_, "f"));
1375 MutableTuple caches(&scope, f.caches());
1376 // Expect that FOR_ITER is the only cached opcode in f().
1377 ASSERT_EQ(caches.length(), 1 * kIcPointersPerEntry);
1378 EXPECT_EQ(icLookupAttr(*caches, 0, iterator.layoutId()), *iter_next);
1379}
1380
1381TEST_F(IcTest, ForIterUpdateCacheWithNonFunctionDoesntUpdateCache) {
1382 ASSERT_FALSE(runFromCStr(runtime_, R"(
1383def f(container):
1384 for i in container:
1385 return i
1386
1387class Iter:
1388 def get(self):
1389 def next():
1390 return 123
1391 return next
1392 __next__ = property(get)
1393
1394class Container:
1395 def __iter__(self):
1396 return Iter()
1397
1398container = Container()
1399iterator = iter(container)
1400result = f(container)
1401)")
1402 .isError());
1403
1404 HandleScope scope(thread_);
1405 Object result(&scope, mainModuleAt(runtime_, "result"));
1406 EXPECT_TRUE(isIntEqualsWord(*result, 123));
1407
1408 Object iterator(&scope, mainModuleAt(runtime_, "iterator"));
1409 Function f(&scope, mainModuleAt(runtime_, "f"));
1410 MutableTuple caches(&scope, f.caches());
1411 // Expect that FOR_ITER is the only cached opcode in f().
1412 ASSERT_EQ(caches.length(), 1 * kIcPointersPerEntry);
1413 EXPECT_TRUE(icLookupAttr(*caches, 0, iterator.layoutId()).isErrorNotFound());
1414}
1415
1416static RawObject testingFunction(Thread* thread) {
1417 Runtime* runtime = thread->runtime();
1418 HandleScope scope(thread);
1419 Object name(&scope, Str::empty());
1420 Tuple consts(&scope, runtime->emptyTuple());
1421 Tuple names(&scope, runtime->newTupleWith2(name, name));
1422 Code code(&scope,
1423 newCodeWithBytesConstsNames(View<byte>(nullptr, 0), consts, names));
1424 MutableBytes rewritten_bytecode(
1425 &scope, runtime->newMutableBytesUninitialized(4 * kCodeUnitSize));
1426 rewrittenBytecodeOpAtPut(rewritten_bytecode, 0, LOAD_GLOBAL);
1427 rewrittenBytecodeArgAtPut(rewritten_bytecode, 0, 0);
1428 rewrittenBytecodeOpAtPut(rewritten_bytecode, 1, STORE_GLOBAL);
1429 rewrittenBytecodeArgAtPut(rewritten_bytecode, 1, 1);
1430 rewrittenBytecodeOpAtPut(rewritten_bytecode, 2, LOAD_GLOBAL);
1431 rewrittenBytecodeArgAtPut(rewritten_bytecode, 2, 0);
1432 rewrittenBytecodeOpAtPut(rewritten_bytecode, 3, STORE_GLOBAL);
1433 rewrittenBytecodeArgAtPut(rewritten_bytecode, 3, 1);
1434
1435 Module module(&scope, findMainModule(runtime));
1436 Function function(&scope,
1437 runtime->newFunctionWithCode(thread, name, code, module));
1438 function.setRewrittenBytecode(*rewritten_bytecode);
1439
1440 MutableTuple caches(&scope, runtime->newMutableTuple(2));
1441 caches.fill(NoneType::object());
1442 function.setCaches(*caches);
1443 return *function;
1444}
1445
1446TEST_F(IcTest,
1447 IcInsertDependentToValueCellDependencyLinkInsertsDependentAsHead) {
1448 HandleScope scope(thread_);
1449 Function function0(&scope, testingFunction(thread_));
1450 Function function1(&scope, testingFunction(thread_));
1451
1452 ValueCell cache(&scope, runtime_->newValueCell());
1453 ASSERT_TRUE(cache.dependencyLink().isNoneType());
1454
1455 EXPECT_TRUE(
1456 icInsertDependentToValueCellDependencyLink(thread_, function0, cache));
1457 WeakLink link0(&scope, cache.dependencyLink());
1458 EXPECT_EQ(link0.referent(), *function0);
1459 EXPECT_TRUE(link0.prev().isNoneType());
1460 EXPECT_TRUE(link0.next().isNoneType());
1461
1462 EXPECT_TRUE(
1463 icInsertDependentToValueCellDependencyLink(thread_, function1, cache));
1464 WeakLink link1(&scope, cache.dependencyLink());
1465 EXPECT_EQ(link1.referent(), *function1);
1466 EXPECT_TRUE(link1.prev().isNoneType());
1467 EXPECT_EQ(link1.next(), *link0);
1468}
1469
1470TEST_F(
1471 IcTest,
1472 IcInsertDependentToValueCellDependencyLinkDoesNotInsertExistingDependent) {
1473 HandleScope scope(thread_);
1474 Function function0(&scope, testingFunction(thread_));
1475 Function function1(&scope, testingFunction(thread_));
1476
1477 ValueCell cache(&scope, runtime_->newValueCell());
1478 EXPECT_TRUE(
1479 icInsertDependentToValueCellDependencyLink(thread_, function0, cache));
1480 EXPECT_TRUE(
1481 icInsertDependentToValueCellDependencyLink(thread_, function1, cache));
1482 EXPECT_FALSE(
1483 icInsertDependentToValueCellDependencyLink(thread_, function0, cache));
1484
1485 WeakLink link(&scope, cache.dependencyLink());
1486 EXPECT_EQ(link.referent(), *function1);
1487 EXPECT_TRUE(link.prev().isNoneType());
1488 EXPECT_EQ(WeakLink::cast(link.next()).referent(), *function0);
1489 EXPECT_TRUE(WeakLink::cast(link.next()).next().isNoneType());
1490}
1491
1492TEST_F(IcTest, IcUpdateGlobalVarFillsCacheLineAndReplaceOpcode) {
1493 HandleScope scope(thread_);
1494 Function function(&scope, testingFunction(thread_));
1495 MutableTuple caches(&scope, function.caches());
1496 MutableBytes rewritten_bytecode(&scope, function.rewrittenBytecode());
1497
1498 ValueCell cache(&scope, runtime_->newValueCell());
1499 cache.setValue(SmallInt::fromWord(99));
1500 ValueCell another_cache(&scope, runtime_->newValueCell());
1501 another_cache.setValue(SmallInt::fromWord(123));
1502
1503 icUpdateGlobalVar(thread_, function, 0, cache);
1504
1505 EXPECT_EQ(caches.at(0), cache);
1506 EXPECT_EQ(rewrittenBytecodeOpAt(rewritten_bytecode, 0), LOAD_GLOBAL_CACHED);
1507 EXPECT_EQ(rewrittenBytecodeOpAt(rewritten_bytecode, 1), STORE_GLOBAL);
1508
1509 icUpdateGlobalVar(thread_, function, 1, another_cache);
1510
1511 EXPECT_EQ(caches.at(0), cache);
1512 EXPECT_EQ(rewrittenBytecodeOpAt(rewritten_bytecode, 0), LOAD_GLOBAL_CACHED);
1513 EXPECT_EQ(rewrittenBytecodeOpAt(rewritten_bytecode, 1), STORE_GLOBAL_CACHED);
1514}
1515
1516TEST_F(IcTest, IcUpdateGlobalVarFillsCacheLineAndReplaceOpcodeWithExtendedArg) {
1517 HandleScope scope(thread_);
1518 Function function(&scope, testingFunction(thread_));
1519 MutableTuple caches(&scope, function.caches());
1520
1521 MutableBytes rewritten_bytecode(
1522 &scope, runtime_->newMutableBytesUninitialized(4 * kCodeUnitSize));
1523 // TODO(T45440363): Replace the argument of EXTENDED_ARG for a non-zero value.
1524 rewrittenBytecodeOpAtPut(rewritten_bytecode, 0, EXTENDED_ARG);
1525 rewrittenBytecodeArgAtPut(rewritten_bytecode, 0, 0);
1526 rewrittenBytecodeOpAtPut(rewritten_bytecode, 1, LOAD_GLOBAL);
1527 rewrittenBytecodeArgAtPut(rewritten_bytecode, 1, 0);
1528 rewrittenBytecodeOpAtPut(rewritten_bytecode, 2, EXTENDED_ARG);
1529 rewrittenBytecodeArgAtPut(rewritten_bytecode, 2, 0);
1530 rewrittenBytecodeOpAtPut(rewritten_bytecode, 3, STORE_GLOBAL);
1531 rewrittenBytecodeArgAtPut(rewritten_bytecode, 3, 1);
1532 function.setRewrittenBytecode(*rewritten_bytecode);
1533
1534 ValueCell cache(&scope, runtime_->newValueCell());
1535 cache.setValue(SmallInt::fromWord(99));
1536 ValueCell another_cache(&scope, runtime_->newValueCell());
1537 another_cache.setValue(SmallInt::fromWord(123));
1538
1539 icUpdateGlobalVar(thread_, function, 0, cache);
1540
1541 EXPECT_EQ(caches.at(0), cache);
1542 EXPECT_EQ(rewrittenBytecodeOpAt(rewritten_bytecode, 1), LOAD_GLOBAL_CACHED);
1543 EXPECT_EQ(rewrittenBytecodeOpAt(rewritten_bytecode, 3), STORE_GLOBAL);
1544
1545 icUpdateGlobalVar(thread_, function, 1, another_cache);
1546
1547 EXPECT_EQ(caches.at(0), cache);
1548 EXPECT_EQ(rewrittenBytecodeOpAt(rewritten_bytecode, 1), LOAD_GLOBAL_CACHED);
1549 EXPECT_EQ(rewrittenBytecodeOpAt(rewritten_bytecode, 3), STORE_GLOBAL_CACHED);
1550}
1551
1552TEST_F(IcTest, IcUpdateGlobalVarCreatesDependencyLink) {
1553 HandleScope scope(thread_);
1554 Function function(&scope, testingFunction(thread_));
1555 ValueCell cache(&scope, runtime_->newValueCell());
1556 cache.setValue(SmallInt::fromWord(99));
1557 icUpdateGlobalVar(thread_, function, 0, cache);
1558
1559 ASSERT_TRUE(cache.dependencyLink().isWeakLink());
1560 WeakLink link(&scope, cache.dependencyLink());
1561 EXPECT_EQ(link.referent(), *function);
1562 EXPECT_EQ(link.prev(), NoneType::object());
1563 EXPECT_EQ(link.next(), NoneType::object());
1564}
1565
1566TEST_F(IcTest, IcUpdateGlobalVarInsertsHeadOfDependencyLink) {
1567 HandleScope scope(thread_);
1568 Function function0(&scope, testingFunction(thread_));
1569 Function function1(&scope, testingFunction(thread_));
1570
1571 // Adds cache into function0's caches first, then to function1's.
1572 ValueCell cache(&scope, runtime_->newValueCell());
1573 cache.setValue(SmallInt::fromWord(99));
1574 icUpdateGlobalVar(thread_, function0, 0, cache);
1575 icUpdateGlobalVar(thread_, function1, 0, cache);
1576
1577 ASSERT_TRUE(cache.dependencyLink().isWeakLink());
1578 WeakLink link(&scope, cache.dependencyLink());
1579 EXPECT_EQ(link.referent(), *function1);
1580 EXPECT_TRUE(link.prev().isNoneType());
1581
1582 WeakLink next_link(&scope, link.next());
1583 EXPECT_EQ(next_link.referent(), *function0);
1584 EXPECT_EQ(next_link.prev(), *link);
1585 EXPECT_TRUE(next_link.next().isNoneType());
1586}
1587
1588TEST_F(IcTest,
1589 IcInvalidateGlobalVarRemovesInvalidatedCacheFromReferencedFunctions) {
1590 HandleScope scope(thread_);
1591 Function function0(&scope, testingFunction(thread_));
1592 Function function1(&scope, testingFunction(thread_));
1593 MutableTuple caches0(&scope, function0.caches());
1594 MutableTuple caches1(&scope, function1.caches());
1595
1596 // Both caches of Function0 & 1 caches the same cache value.
1597 ValueCell cache(&scope, runtime_->newValueCell());
1598 cache.setValue(SmallInt::fromWord(99));
1599 ValueCell another_cache(&scope, runtime_->newValueCell());
1600 another_cache.setValue(SmallInt::fromWord(123));
1601
1602 icUpdateGlobalVar(thread_, function0, 0, cache);
1603 icUpdateGlobalVar(thread_, function0, 1, another_cache);
1604 icUpdateGlobalVar(thread_, function1, 0, another_cache);
1605 icUpdateGlobalVar(thread_, function1, 1, cache);
1606
1607 EXPECT_TRUE(
1608 isIntEqualsWord(valueCellValue(icLookupGlobalVar(*caches0, 0)), 99));
1609 EXPECT_TRUE(
1610 isIntEqualsWord(valueCellValue(icLookupGlobalVar(*caches0, 1)), 123));
1611 EXPECT_TRUE(
1612 isIntEqualsWord(valueCellValue(icLookupGlobalVar(*caches1, 0)), 123));
1613 EXPECT_TRUE(
1614 isIntEqualsWord(valueCellValue(icLookupGlobalVar(*caches1, 1)), 99));
1615
1616 // Invalidating cache makes it removed from both caches, and nobody depends on
1617 // it anymore.
1618 icInvalidateGlobalVar(thread_, cache);
1619
1620 EXPECT_TRUE(icLookupGlobalVar(*caches0, 0).isNoneType());
1621 EXPECT_TRUE(
1622 isIntEqualsWord(valueCellValue(icLookupGlobalVar(*caches0, 1)), 123));
1623 EXPECT_TRUE(
1624 isIntEqualsWord(valueCellValue(icLookupGlobalVar(*caches1, 0)), 123));
1625 EXPECT_TRUE(icLookupGlobalVar(*caches1, 1).isNoneType());
1626 EXPECT_TRUE(cache.dependencyLink().isNoneType());
1627}
1628
1629TEST_F(IcTest, IcInvalidateGlobalVarDoNotDeferenceDeallocatedReferent) {
1630 HandleScope scope(thread_);
1631 Function function0(&scope, testingFunction(thread_));
1632 Function function1(&scope, testingFunction(thread_));
1633 MutableTuple caches0(&scope, function0.caches());
1634 MutableTuple caches1(&scope, function1.caches());
1635
1636 // Both caches of Function0 & 1 caches the same cache value.
1637 ValueCell cache(&scope, runtime_->newValueCell());
1638 cache.setValue(SmallInt::fromWord(99));
1639 ValueCell another_cache(&scope, runtime_->newValueCell());
1640 another_cache.setValue(SmallInt::fromWord(123));
1641
1642 icUpdateGlobalVar(thread_, function0, 0, cache);
1643 icUpdateGlobalVar(thread_, function0, 1, another_cache);
1644 icUpdateGlobalVar(thread_, function1, 0, another_cache);
1645 icUpdateGlobalVar(thread_, function1, 1, cache);
1646
1647 ASSERT_TRUE(
1648 isIntEqualsWord(valueCellValue(icLookupGlobalVar(*caches0, 0)), 99));
1649 ASSERT_TRUE(
1650 isIntEqualsWord(valueCellValue(icLookupGlobalVar(*caches0, 1)), 123));
1651 ASSERT_TRUE(
1652 isIntEqualsWord(valueCellValue(icLookupGlobalVar(*caches1, 0)), 123));
1653 ASSERT_TRUE(
1654 isIntEqualsWord(valueCellValue(icLookupGlobalVar(*caches1, 1)), 99));
1655
1656 // Simulate GCing function1.
1657 WeakLink link(&scope, cache.dependencyLink());
1658 ASSERT_EQ(link.referent(), *function1);
1659 link.setReferent(NoneType::object());
1660
1661 // Invalidation cannot touch function1 anymore.
1662 icInvalidateGlobalVar(thread_, cache);
1663
1664 EXPECT_TRUE(icLookupGlobalVar(*caches0, 0).isNoneType());
1665 EXPECT_TRUE(
1666 isIntEqualsWord(valueCellValue(icLookupGlobalVar(*caches0, 1)), 123));
1667 EXPECT_TRUE(
1668 isIntEqualsWord(valueCellValue(icLookupGlobalVar(*caches1, 0)), 123));
1669 EXPECT_TRUE(
1670 isIntEqualsWord(valueCellValue(icLookupGlobalVar(*caches1, 1)), 99));
1671 EXPECT_TRUE(cache.dependencyLink().isNoneType());
1672}
1673
1674TEST_F(IcTest, IcInvalidateGlobalVarRevertsOpCodeToOriginalOnes) {
1675 HandleScope scope(thread_);
1676 Function function(&scope, testingFunction(thread_));
1677 MutableBytes bytecode(&scope, function.rewrittenBytecode());
1678 ValueCell cache(&scope, runtime_->newValueCell());
1679 cache.setValue(SmallInt::fromWord(99));
1680 ValueCell another_cache(&scope, runtime_->newValueCell());
1681 another_cache.setValue(SmallInt::fromWord(123));
1682
1683 byte original_expected[] = {LOAD_GLOBAL, 0, 0, 0, STORE_GLOBAL, 1, 0, 0,
1684 LOAD_GLOBAL, 0, 0, 0, STORE_GLOBAL, 1, 0, 0};
1685 ASSERT_TRUE(isMutableBytesEqualsBytes(bytecode, original_expected));
1686
1687 icUpdateGlobalVar(thread_, function, 0, cache);
1688 byte cached_expected0[] = {
1689 LOAD_GLOBAL_CACHED, 0, 0, 0, STORE_GLOBAL, 1, 0, 0,
1690 LOAD_GLOBAL_CACHED, 0, 0, 0, STORE_GLOBAL, 1, 0, 0};
1691 EXPECT_TRUE(isMutableBytesEqualsBytes(bytecode, cached_expected0));
1692
1693 icUpdateGlobalVar(thread_, function, 1, another_cache);
1694 byte cached_expected1[] = {
1695 LOAD_GLOBAL_CACHED, 0, 0, 0, STORE_GLOBAL_CACHED, 1, 0, 0,
1696 LOAD_GLOBAL_CACHED, 0, 0, 0, STORE_GLOBAL_CACHED, 1, 0, 0};
1697 EXPECT_TRUE(isMutableBytesEqualsBytes(bytecode, cached_expected1));
1698
1699 icInvalidateGlobalVar(thread_, cache);
1700
1701 // Only invalidated cache's opcode gets reverted to the original one.
1702 byte invalidated_expected[] = {
1703 LOAD_GLOBAL, 0, 0, 0, STORE_GLOBAL_CACHED, 1, 0, 0,
1704 LOAD_GLOBAL, 0, 0, 0, STORE_GLOBAL_CACHED, 1, 0, 0,
1705 };
1706 EXPECT_TRUE(isMutableBytesEqualsBytes(bytecode, invalidated_expected));
1707}
1708
1709TEST_F(IcTest, IcIteratorIteratesOverAttrCaches) {
1710 HandleScope scope(thread_);
1711 MutableBytes bytecode(
1712 &scope, runtime_->newMutableBytesUninitialized(10 * kCodeUnitSize));
1713 rewrittenBytecodeOpAtPut(bytecode, 0, LOAD_GLOBAL);
1714 rewrittenBytecodeArgAtPut(bytecode, 0, 100);
1715 rewrittenBytecodeCacheAtPut(bytecode, 0, 0);
1716 rewrittenBytecodeOpAtPut(bytecode, 1, LOAD_ATTR_ANAMORPHIC);
1717 rewrittenBytecodeArgAtPut(bytecode, 1, 0);
1718 rewrittenBytecodeCacheAtPut(bytecode, 1, 0);
1719 rewrittenBytecodeOpAtPut(bytecode, 2, LOAD_GLOBAL);
1720 rewrittenBytecodeArgAtPut(bytecode, 2, 100);
1721 rewrittenBytecodeCacheAtPut(bytecode, 2, 0);
1722 rewrittenBytecodeOpAtPut(bytecode, 3, LOAD_METHOD_ANAMORPHIC);
1723 rewrittenBytecodeArgAtPut(bytecode, 3, 1);
1724 rewrittenBytecodeCacheAtPut(bytecode, 3, 1);
1725 rewrittenBytecodeOpAtPut(bytecode, 4, LOAD_GLOBAL);
1726 rewrittenBytecodeArgAtPut(bytecode, 4, 100);
1727 rewrittenBytecodeCacheAtPut(bytecode, 4, 0);
1728 rewrittenBytecodeOpAtPut(bytecode, 5, LOAD_ATTR_ANAMORPHIC);
1729 rewrittenBytecodeArgAtPut(bytecode, 5, 2);
1730 rewrittenBytecodeCacheAtPut(bytecode, 5, 2);
1731 rewrittenBytecodeOpAtPut(bytecode, 6, STORE_ATTR_ANAMORPHIC);
1732 rewrittenBytecodeArgAtPut(bytecode, 6, 3);
1733 rewrittenBytecodeCacheAtPut(bytecode, 6, 3);
1734 rewrittenBytecodeOpAtPut(bytecode, 7, FOR_ITER_ANAMORPHIC);
1735 rewrittenBytecodeArgAtPut(bytecode, 7, -1);
1736 rewrittenBytecodeCacheAtPut(bytecode, 7, 4);
1737 rewrittenBytecodeOpAtPut(bytecode, 8, BINARY_SUBSCR_ANAMORPHIC);
1738 rewrittenBytecodeArgAtPut(bytecode, 8, -1);
1739 rewrittenBytecodeCacheAtPut(bytecode, 8, 5);
1740 rewrittenBytecodeOpAtPut(bytecode, 9, LOAD_GLOBAL);
1741 rewrittenBytecodeArgAtPut(bytecode, 9, 100);
1742 rewrittenBytecodeCacheAtPut(bytecode, 9, 0);
1743
1744 word num_caches = 6;
1745
1746 Object name1(&scope, Runtime::internStrFromCStr(
1747 thread_, "load_attr_cached_attr_name"));
1748 Object name2(&scope, Runtime::internStrFromCStr(
1749 thread_, "load_method_cached_attr_name"));
1750 Object name3(&scope, Runtime::internStrFromCStr(
1751 thread_, "load_attr_cached_attr_name2"));
1752 Object name4(&scope, Runtime::internStrFromCStr(
1753 thread_, "store_attr_cached_attr_name"));
1754 Tuple names(&scope, runtime_->newTupleWith4(name1, name2, name3, name4));
1755
1756 Object name(&scope, runtime_->newStrFromCStr("name"));
1757 Function dependent(&scope, newEmptyFunction());
1758 Object value(&scope, NoneType::object());
1759 MutableTuple caches(
1760 &scope, runtime_->newMutableTuple(num_caches * kIcPointersPerEntry));
1761 caches.fill(NoneType::object());
1762 // Caches for LOAD_ATTR_ANAMORPHIC at PC 2.
1763 value = SmallInt::fromWord(10);
1764 icUpdateAttr(thread_, caches, 0, LayoutId::kBool, value, name, dependent);
1765 value = SmallInt::fromWord(20);
1766 icUpdateAttr(thread_, caches, 0, LayoutId::kSmallInt, value, name, dependent);
1767
1768 // Caches for LOAD_METHOD_ANAMORPHIC at PC 6.
1769 value = SmallInt::fromWord(30);
1770 icUpdateAttr(thread_, caches, 1, LayoutId::kSmallInt, value, name, dependent);
1771
1772 // Caches are empty for LOAD_ATTR_ANAMORPHIC at PC 10.
1773
1774 // Caches for STORE_ATTR_ANAMORPHIC at PC 12.
1775 value = SmallInt::fromWord(40);
1776 icUpdateAttr(thread_, caches, 3, LayoutId::kNoneType, value, name, dependent);
1777
1778 // Caches for FOR_ITER_ANAMORPHIC at PC 14.
1779 value = SmallInt::fromWord(50);
1780 icUpdateAttr(thread_, caches, 4, LayoutId::kStr, value, name, dependent);
1781
1782 // Caches for BINARY_SUBSCR_ANAMORPHIC at PC 16.
1783 value = SmallInt::fromWord(60);
1784 icUpdateAttr(thread_, caches, 5, LayoutId::kTuple, value, name, dependent);
1785
1786 Function function(&scope, newEmptyFunction());
1787 function.setRewrittenBytecode(*bytecode);
1788 function.setCaches(*caches);
1789 Code::cast(function.code()).setNames(*names);
1790
1791 IcIterator it(&scope, runtime_, *function);
1792 ASSERT_TRUE(it.hasNext());
1793 ASSERT_TRUE(it.isAttrCache());
1794 EXPECT_FALSE(it.isBinaryOpCache());
1795 Object load_attr_cached_attr_name(
1796 &scope,
1797 Runtime::internStrFromCStr(thread_, "load_attr_cached_attr_name"));
1798 EXPECT_TRUE(it.isAttrNameEqualTo(load_attr_cached_attr_name));
1799 EXPECT_EQ(it.layoutId(), LayoutId::kBool);
1800 EXPECT_TRUE(it.isInstanceAttr());
1801
1802 it.next();
1803 ASSERT_TRUE(it.hasNext());
1804 ASSERT_TRUE(it.isAttrCache());
1805 EXPECT_FALSE(it.isBinaryOpCache());
1806 EXPECT_TRUE(it.isAttrNameEqualTo(load_attr_cached_attr_name));
1807 EXPECT_EQ(it.layoutId(), LayoutId::kSmallInt);
1808 EXPECT_TRUE(it.isInstanceAttr());
1809
1810 it.next();
1811 ASSERT_TRUE(it.hasNext());
1812 ASSERT_TRUE(it.isAttrCache());
1813 EXPECT_FALSE(it.isBinaryOpCache());
1814 Object load_method_cached_attr_name(
1815 &scope,
1816 Runtime::internStrFromCStr(thread_, "load_method_cached_attr_name"));
1817 EXPECT_TRUE(it.isAttrNameEqualTo(load_method_cached_attr_name));
1818 EXPECT_EQ(it.layoutId(), SmallInt::fromWord(100).layoutId());
1819 EXPECT_TRUE(it.isInstanceAttr());
1820
1821 it.next();
1822 ASSERT_TRUE(it.hasNext());
1823 ASSERT_TRUE(it.isAttrCache());
1824 EXPECT_FALSE(it.isBinaryOpCache());
1825 Object store_attr_cached_attr_name(
1826 &scope,
1827 Runtime::internStrFromCStr(thread_, "store_attr_cached_attr_name"));
1828 EXPECT_TRUE(it.isAttrNameEqualTo(store_attr_cached_attr_name));
1829 EXPECT_EQ(it.layoutId(), NoneType::object().layoutId());
1830 EXPECT_TRUE(it.isInstanceAttr());
1831
1832 ASSERT_EQ(
1833 caches.at(3 * kIcPointersPerEntry + kIcEntryKeyOffset),
1834 SmallInt::fromWord(static_cast<word>(NoneType::object().layoutId())));
1835 ASSERT_FALSE(
1836 caches.at(3 * kIcPointersPerEntry + kIcEntryValueOffset).isNoneType());
1837
1838 it.evict();
1839
1840 EXPECT_TRUE(
1841 caches.at(3 * kIcPointersPerEntry + kIcEntryKeyOffset).isNoneType());
1842 EXPECT_TRUE(
1843 caches.at(3 * kIcPointersPerEntry + kIcEntryValueOffset).isNoneType());
1844
1845 it.next();
1846 ASSERT_TRUE(it.hasNext());
1847 ASSERT_TRUE(it.isAttrCache());
1848 EXPECT_FALSE(it.isBinaryOpCache());
1849 Object for_iter_cached_attr_name(
1850 &scope, Runtime::internStrFromCStr(thread_, "__next__"));
1851 EXPECT_TRUE(it.isAttrNameEqualTo(for_iter_cached_attr_name));
1852 EXPECT_EQ(it.layoutId(), LayoutId::kStr);
1853 EXPECT_TRUE(it.isInstanceAttr());
1854
1855 it.next();
1856 ASSERT_TRUE(it.hasNext());
1857 ASSERT_TRUE(it.isAttrCache());
1858 EXPECT_FALSE(it.isBinaryOpCache());
1859 Object binary_subscr_cached_attr_name(
1860 &scope, Runtime::internStrFromCStr(thread_, "__getitem__"));
1861 EXPECT_TRUE(it.isAttrNameEqualTo(binary_subscr_cached_attr_name));
1862 EXPECT_EQ(it.layoutId(), LayoutId::kTuple);
1863 EXPECT_TRUE(it.isInstanceAttr());
1864
1865 it.next();
1866 EXPECT_FALSE(it.hasNext());
1867}
1868
1869TEST_F(IcTest, IcIteratorIteratesOverBinaryOpCaches) {
1870 HandleScope scope(thread_);
1871 MutableBytes bytecode(
1872 &scope, runtime_->newMutableBytesUninitialized(4 * kCodeUnitSize));
1873 rewrittenBytecodeOpAtPut(bytecode, 0, LOAD_GLOBAL);
1874 rewrittenBytecodeArgAtPut(bytecode, 0, 100);
1875 rewrittenBytecodeCacheAtPut(bytecode, 0, 0);
1876 rewrittenBytecodeOpAtPut(bytecode, 1, COMPARE_OP_ANAMORPHIC);
1877 rewrittenBytecodeArgAtPut(bytecode, 1, CompareOp::GE);
1878 rewrittenBytecodeCacheAtPut(bytecode, 1, 0);
1879 rewrittenBytecodeOpAtPut(bytecode, 2, BINARY_OP_ANAMORPHIC);
1880 rewrittenBytecodeArgAtPut(bytecode, 2,
1881 static_cast<word>(Interpreter::BinaryOp::ADD));
1882 rewrittenBytecodeCacheAtPut(bytecode, 2, 1);
1883 rewrittenBytecodeOpAtPut(bytecode, 3, LOAD_GLOBAL);
1884 rewrittenBytecodeArgAtPut(bytecode, 3, 100);
1885 rewrittenBytecodeCacheAtPut(bytecode, 3, 0);
1886
1887 word num_caches = 2;
1888 MutableTuple caches(
1889 &scope, runtime_->newMutableTuple(num_caches * kIcPointersPerEntry));
1890
1891 // Caches for COMPARE_OP_ANAMORPHIC at 2.
1892 word compare_op_cached_index =
1893 0 * kIcPointersPerEntry + 0 * kIcPointersPerEntry;
1894 word compare_op_key_high_bits =
1895 static_cast<word>(SmallInt::fromWord(0).layoutId())
1896 << Header::kLayoutIdBits |
1897 static_cast<word>(SmallStr::fromCStr("test").layoutId());
1898 caches.atPut(compare_op_cached_index + kIcEntryKeyOffset,
1899 SmallInt::fromWord(compare_op_key_high_bits << kBitsPerByte |
1900 static_cast<word>(kBinaryOpReflected)));
1901 caches.atPut(compare_op_cached_index + kIcEntryValueOffset,
1902 SmallInt::fromWord(50));
1903
1904 // Caches for BINARY_OP_ANAMORPHIC at 4.
1905 word binary_op_cached_index =
1906 1 * kIcPointersPerEntry + 0 * kIcPointersPerEntry;
1907 word binary_op_key_high_bits =
1908 static_cast<word>(SmallStr::fromCStr("").layoutId())
1909 << Header::kLayoutIdBits |
1910 static_cast<word>(SmallInt::fromWord(0).layoutId());
1911 caches.atPut(binary_op_cached_index + kIcEntryKeyOffset,
1912 SmallInt::fromWord(binary_op_key_high_bits << kBitsPerByte |
1913 static_cast<word>(kBinaryOpReflected)));
1914 caches.atPut(binary_op_cached_index + kIcEntryValueOffset,
1915 SmallInt::fromWord(60));
1916
1917 Function function(&scope, newEmptyFunction());
1918 function.setRewrittenBytecode(*bytecode);
1919 function.setCaches(*caches);
1920
1921 IcIterator it(&scope, runtime_, *function);
1922 ASSERT_TRUE(it.hasNext());
1923 ASSERT_TRUE(it.isBinaryOpCache());
1924 EXPECT_FALSE(it.isAttrCache());
1925 EXPECT_EQ(it.leftLayoutId(), SmallInt::fromWord(-1).layoutId());
1926 EXPECT_EQ(it.rightLayoutId(), SmallStr::fromCStr("").layoutId());
1927 {
1928 Object left_operator_name(&scope,
1929 Runtime::internStrFromCStr(thread_, "__ge__"));
1930 EXPECT_EQ(left_operator_name, it.leftMethodName());
1931 Object right_operator_name(&scope,
1932 Runtime::internStrFromCStr(thread_, "__le__"));
1933 EXPECT_EQ(right_operator_name, it.rightMethodName());
1934 }
1935
1936 it.next();
1937 ASSERT_TRUE(it.hasNext());
1938 ASSERT_TRUE(it.isBinaryOpCache());
1939 EXPECT_FALSE(it.isAttrCache());
1940 EXPECT_EQ(it.leftLayoutId(), SmallStr::fromCStr("").layoutId());
1941 EXPECT_EQ(it.rightLayoutId(), SmallInt::fromWord(-1).layoutId());
1942 {
1943 Object left_operator_name(&scope,
1944 Runtime::internStrFromCStr(thread_, "__add__"));
1945 EXPECT_EQ(left_operator_name, it.leftMethodName());
1946 Object right_operator_name(&scope,
1947 Runtime::internStrFromCStr(thread_, "__radd__"));
1948 EXPECT_EQ(right_operator_name, it.rightMethodName());
1949 }
1950
1951 it.next();
1952 EXPECT_FALSE(it.hasNext());
1953}
1954
1955TEST_F(IcTest, IcIteratorIteratesOverInplaceOpCaches) {
1956 HandleScope scope(thread_);
1957 MutableBytes bytecode(
1958 &scope, runtime_->newMutableBytesUninitialized(4 * kCodeUnitSize));
1959 rewrittenBytecodeOpAtPut(bytecode, 0, LOAD_GLOBAL);
1960 rewrittenBytecodeArgAtPut(bytecode, 0, 100);
1961 rewrittenBytecodeCacheAtPut(bytecode, 0, 0);
1962 rewrittenBytecodeOpAtPut(bytecode, 1, INPLACE_OP_ANAMORPHIC);
1963 rewrittenBytecodeArgAtPut(bytecode, 1,
1964 static_cast<word>(Interpreter::BinaryOp::MUL));
1965 rewrittenBytecodeCacheAtPut(bytecode, 1, 0);
1966 rewrittenBytecodeOpAtPut(bytecode, 2, LOAD_GLOBAL);
1967 rewrittenBytecodeArgAtPut(bytecode, 2, 100);
1968 rewrittenBytecodeCacheAtPut(bytecode, 2, 0);
1969
1970 word num_caches = 1;
1971 MutableTuple caches(
1972 &scope, runtime_->newMutableTuple(num_caches * kIcPointersPerEntry));
1973
1974 // Caches for BINARY_OP_ANAMORPHIC at 2.
1975 word inplace_op_cached_index =
1976 0 * kIcPointersPerEntry + 0 * kIcPointersPerEntry;
1977 word inplace_op_key_high_bits =
1978 static_cast<word>(SmallStr::fromCStr("a").layoutId())
1979 << Header::kLayoutIdBits |
1980 static_cast<word>(SmallInt::fromWord(3).layoutId());
1981 caches.atPut(inplace_op_cached_index + kIcEntryKeyOffset,
1982 SmallInt::fromWord(inplace_op_key_high_bits << kBitsPerByte |
1983 static_cast<word>(kBinaryOpReflected)));
1984 caches.atPut(inplace_op_cached_index + kIcEntryValueOffset,
1985 SmallInt::fromWord(70));
1986
1987 Function function(&scope, newEmptyFunction());
1988 function.setRewrittenBytecode(*bytecode);
1989 function.setCaches(*caches);
1990
1991 IcIterator it(&scope, runtime_, *function);
1992 ASSERT_TRUE(it.hasNext());
1993 ASSERT_TRUE(it.isInplaceOpCache());
1994 EXPECT_FALSE(it.isBinaryOpCache());
1995 EXPECT_FALSE(it.isAttrCache());
1996 EXPECT_EQ(it.leftLayoutId(), SmallStr::fromCStr("").layoutId());
1997 EXPECT_EQ(it.rightLayoutId(), SmallInt::fromWord(-1).layoutId());
1998 {
1999 Object inplace_operator_name(
2000 &scope, Runtime::internStrFromCStr(thread_, "__imul__"));
2001 EXPECT_EQ(inplace_operator_name, it.inplaceMethodName());
2002 Object left_operator_name(&scope,
2003 Runtime::internStrFromCStr(thread_, "__mul__"));
2004 EXPECT_EQ(left_operator_name, it.leftMethodName());
2005 Object right_operator_name(&scope,
2006 Runtime::internStrFromCStr(thread_, "__rmul__"));
2007 EXPECT_EQ(right_operator_name, it.rightMethodName());
2008 }
2009
2010 it.next();
2011 EXPECT_FALSE(it.hasNext());
2012}
2013
2014TEST_F(IcTest, IcRemoveDeadWeakLinksRemoveRemovesDeadHead) {
2015 HandleScope scope(thread_);
2016 ValueCell value_cell(&scope, runtime_->newValueCell());
2017 Object dependent1(&scope, newTupleWithNone(1));
2018 Object dependent2(&scope, newTupleWithNone(2));
2019 Object dependent3(&scope, newTupleWithNone(3));
2020 icInsertDependentToValueCellDependencyLink(thread_, dependent1, value_cell);
2021 icInsertDependentToValueCellDependencyLink(thread_, dependent2, value_cell);
2022 icInsertDependentToValueCellDependencyLink(thread_, dependent3, value_cell);
2023 // The dependenty link looks like dependent3 -> dependent2 -> dependent1.
2024 // Kill dependent3.
2025 WeakLink head(&scope, value_cell.dependencyLink());
2026 head.setReferent(NoneType::object());
2027
2028 icRemoveDeadWeakLinks(*value_cell);
2029
2030 ASSERT_TRUE(value_cell.dependencyLink().isWeakLink());
2031 WeakLink new_head(&scope, value_cell.dependencyLink());
2032 EXPECT_EQ(new_head.referent(), *dependent2);
2033 EXPECT_TRUE(new_head.prev().isNoneType());
2034 WeakLink new_next(&scope, new_head.next());
2035 EXPECT_EQ(new_next.referent(), *dependent1);
2036 EXPECT_EQ(new_next.prev(), *new_head);
2037 EXPECT_TRUE(new_next.next().isNoneType());
2038}
2039
2040TEST_F(IcTest, IcRemoveDeadWeakLinksRemoveRemovesDeadMiddleNode) {
2041 HandleScope scope(thread_);
2042 ValueCell value_cell(&scope, runtime_->newValueCell());
2043 Object dependent1(&scope, newTupleWithNone(1));
2044 Object dependent2(&scope, newTupleWithNone(2));
2045 Object dependent3(&scope, newTupleWithNone(3));
2046 icInsertDependentToValueCellDependencyLink(thread_, dependent1, value_cell);
2047 icInsertDependentToValueCellDependencyLink(thread_, dependent2, value_cell);
2048 icInsertDependentToValueCellDependencyLink(thread_, dependent3, value_cell);
2049 // The dependenty link looks like dependent3 -> dependent2 -> dependent1.
2050 WeakLink head(&scope, value_cell.dependencyLink());
2051 // Kill dependent2.
2052 WeakLink next(&scope, head.next());
2053 next.setReferent(NoneType::object());
2054
2055 icRemoveDeadWeakLinks(*value_cell);
2056
2057 ASSERT_TRUE(value_cell.dependencyLink().isWeakLink());
2058 WeakLink new_head(&scope, value_cell.dependencyLink());
2059 EXPECT_EQ(new_head.referent(), *dependent3);
2060 EXPECT_TRUE(new_head.prev().isNoneType());
2061 WeakLink new_next(&scope, new_head.next());
2062 EXPECT_EQ(new_next.referent(), *dependent1);
2063 EXPECT_EQ(new_next.prev(), *new_head);
2064 EXPECT_TRUE(new_next.next().isNoneType());
2065}
2066
2067TEST_F(IcTest, IcRemoveDeadWeakLinksRemoveRemovesDeadTailNode) {
2068 HandleScope scope(thread_);
2069 ValueCell value_cell(&scope, runtime_->newValueCell());
2070 Object dependent1(&scope, newTupleWithNone(1));
2071 Object dependent2(&scope, newTupleWithNone(2));
2072 Object dependent3(&scope, newTupleWithNone(3));
2073 icInsertDependentToValueCellDependencyLink(thread_, dependent1, value_cell);
2074 icInsertDependentToValueCellDependencyLink(thread_, dependent2, value_cell);
2075 icInsertDependentToValueCellDependencyLink(thread_, dependent3, value_cell);
2076 // The dependenty link looks like dependent3 -> dependent2 -> dependent1.
2077 WeakLink head(&scope, value_cell.dependencyLink());
2078 // Kill dependent1.
2079 WeakLink next_next(&scope, WeakLink::cast(head.next()).next());
2080 next_next.setReferent(NoneType::object());
2081
2082 icRemoveDeadWeakLinks(*value_cell);
2083
2084 ASSERT_TRUE(value_cell.dependencyLink().isWeakLink());
2085 WeakLink new_head(&scope, value_cell.dependencyLink());
2086 EXPECT_EQ(new_head.referent(), *dependent3);
2087 EXPECT_TRUE(new_head.prev().isNoneType());
2088 WeakLink new_next(&scope, new_head.next());
2089 EXPECT_EQ(new_next.referent(), *dependent2);
2090 EXPECT_EQ(new_next.prev(), *new_head);
2091 EXPECT_TRUE(new_next.next().isNoneType());
2092}
2093
2094TEST_F(IcTest,
2095 EncodeBinaryOpKeyEntryReturnsKeyAccessedByLookupBinOpMonomorphic) {
2096 HandleScope scope(thread_);
2097 SmallInt entry_key(&scope, encodeBinaryOpKey(LayoutId::kStr, LayoutId::kInt,
2098 kBinaryOpReflected));
2099 Object entry_value(&scope, runtime_->newStrFromCStr("value"));
2100 MutableTuple caches(&scope, runtime_->newMutableTuple(kIcPointersPerEntry));
2101 caches.fill(NoneType::object());
2102 caches.atPut(kIcEntryKeyOffset, *entry_key);
2103 caches.atPut(kIcEntryValueOffset, *entry_value);
2104
2105 BinaryOpFlags flags_out;
2106 Object result(&scope, icLookupBinOpMonomorphic(*caches, 0, LayoutId::kStr,
2107 LayoutId::kInt, &flags_out));
2108 EXPECT_EQ(result, entry_value);
2109 EXPECT_TRUE(icLookupBinOpMonomorphic(*caches, 0, LayoutId::kInt,
2110 LayoutId::kStr, &flags_out)
2111 .isErrorNotFound());
2112}
2113
2114TEST_F(
2115 IcTest,
2116 IcInvalidateAttrWithDunderFunctionsUpdatesCorrespondingAttributeTypeFlags) {
2117 HandleScope scope(thread_);
2118 ASSERT_FALSE(runFromCStr(runtime_, R"(
2119class A:
2120 pass
2121
2122class B(A):
2123 pass
2124
2125class C(B):
2126 pass
2127
2128class X:
2129 pass
2130
2131class D(X, C):
2132 pass
2133
2134class E(D):
2135 pass
2136
2137def custom_getattribute(self, name):
2138 return "bogus"
2139
2140object_getattribute = object.__getattribute__
2141)")
2142 .isError());
2143
2144 Type a(&scope, mainModuleAt(runtime_, "A"));
2145 Type b(&scope, mainModuleAt(runtime_, "B"));
2146 Type c(&scope, mainModuleAt(runtime_, "C"));
2147 Type d(&scope, mainModuleAt(runtime_, "D"));
2148 Type e(&scope, mainModuleAt(runtime_, "E"));
2149 Type x(&scope, mainModuleAt(runtime_, "X"));
2150
2151 ASSERT_TRUE(a.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
2152 ASSERT_TRUE(b.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
2153 ASSERT_TRUE(c.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
2154 ASSERT_TRUE(d.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
2155 ASSERT_TRUE(e.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
2156 ASSERT_TRUE(x.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
2157
2158 Object custom_getattribute(&scope,
2159 mainModuleAt(runtime_, "custom_getattribute"));
2160 typeAtPutById(thread_, c, ID(__getattribute__), custom_getattribute);
2161
2162 EXPECT_TRUE(a.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
2163 EXPECT_TRUE(b.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
2164 EXPECT_FALSE(c.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
2165 EXPECT_FALSE(d.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
2166 EXPECT_FALSE(e.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
2167 EXPECT_TRUE(x.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
2168
2169 Object object_getattribute(&scope,
2170 mainModuleAt(runtime_, "object_getattribute"));
2171 typeAtPutById(thread_, c, ID(__getattribute__), object_getattribute);
2172
2173 EXPECT_TRUE(a.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
2174 EXPECT_TRUE(b.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
2175 EXPECT_TRUE(c.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
2176 EXPECT_TRUE(d.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
2177 EXPECT_TRUE(e.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
2178 EXPECT_TRUE(x.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
2179}
2180
2181TEST_F(IcTest, IcInvalidateTypeHierarchy) {
2182 ASSERT_FALSE(runFromCStr(runtime_, R"(
2183class A:
2184 def __init__(self):
2185 self.foo = 400
2186
2187class B(A):
2188 pass
2189
2190def cache_attribute(c):
2191 return c.foo
2192
2193def invalidate():
2194 A.foo = property(lambda self: 123)
2195
2196a = A()
2197b = B()
2198a_init = A.__init__
2199)")
2200 .isError());
2201 HandleScope scope(thread_);
2202 Function a_init(&scope, mainModuleAt(runtime_, "a_init"));
2203 Function cache_attribute(&scope, mainModuleAt(runtime_, "cache_attribute"));
2204 Type type_a(&scope, mainModuleAt(runtime_, "A"));
2205 Type type_b(&scope, mainModuleAt(runtime_, "B"));
2206 Object obj_a(&scope, mainModuleAt(runtime_, "a"));
2207 Object obj_b(&scope, mainModuleAt(runtime_, "b"));
2208 Object foo_name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
2209 ValueCell foo_in_a(&scope, typeValueCellAt(*type_a, *foo_name));
2210 ValueCell foo_in_b(&scope, typeValueCellAt(*type_b, *foo_name));
2211
2212 // We've called __init__ already so it should be a dependent.
2213 EXPECT_TRUE(icDependentIncluded(*a_init, foo_in_a.dependencyLink()));
2214 EXPECT_TRUE(icDependentIncluded(*a_init, foo_in_b.dependencyLink()));
2215 EXPECT_FALSE(icDependentIncluded(*cache_attribute, foo_in_a.dependencyLink()));
2216 EXPECT_FALSE(icDependentIncluded(*cache_attribute, foo_in_b.dependencyLink()));
2217
2218 // Cache an attribute elsewhere.
2219 ASSERT_TRUE(isIntEqualsWord(Interpreter::call1(thread_, cache_attribute, obj_a), 400));
2220 ASSERT_TRUE(isIntEqualsWord(Interpreter::call1(thread_, cache_attribute, obj_b), 400));
2221
2222 // That function should be a dependent too.
2223 EXPECT_TRUE(icDependentIncluded(*a_init, foo_in_a.dependencyLink()));
2224 EXPECT_TRUE(icDependentIncluded(*a_init, foo_in_b.dependencyLink()));
2225 EXPECT_TRUE(icDependentIncluded(*cache_attribute, foo_in_a.dependencyLink()));
2226 EXPECT_TRUE(icDependentIncluded(*cache_attribute, foo_in_b.dependencyLink()));
2227
2228 // Invalidate the attribute.
2229 Function invalidate(&scope, mainModuleAt(runtime_, "invalidate"));
2230 ASSERT_TRUE(Interpreter::call0(thread_, invalidate).isNoneType());
2231
2232 EXPECT_FALSE(icDependentIncluded(*a_init, foo_in_a.dependencyLink()));
2233 EXPECT_FALSE(icDependentIncluded(*a_init, foo_in_b.dependencyLink()));
2234 // TODO(#269): Change to EXPECT_FALSE.
2235 EXPECT_TRUE(icDependentIncluded(*cache_attribute, foo_in_a.dependencyLink()));
2236 EXPECT_TRUE(icDependentIncluded(*cache_attribute, foo_in_b.dependencyLink()));
2237}
2238
2239} // namespace testing
2240} // namespace py