@@ -831,6 +831,8 @@ dummy_func(
831831 assert (Py_REFCNT (left_o ) >= 2 || !PyStackRef_IsHeapSafe (left ));
832832 PyStackRef_CLOSE_SPECIALIZED (left , _PyUnicode_ExactDealloc );
833833 DEAD (left );
834+ // REGIONS: No write barrier needed, since these are all unicode
835+ // objects and therefore immutable when observed
834836 PyObject * temp = PyStackRef_AsPyObjectSteal (* target_local );
835837 PyObject * right_o = PyStackRef_AsPyObjectSteal (right );
836838 PyUnicode_Append (& temp , right_o );
@@ -882,6 +884,7 @@ dummy_func(
882884 }
883885
884886 op (_BINARY_SLICE , (container , start , stop -- res )) {
887+ // REGIONS: Write barrier called inside `_PyBuildSlice_ConsumeRefs`
885888 PyObject * slice = _PyBuildSlice_ConsumeRefs (PyStackRef_AsPyObjectSteal (start ),
886889 PyStackRef_AsPyObjectSteal (stop ));
887890 PyObject * res_o ;
@@ -892,6 +895,8 @@ dummy_func(
892895 }
893896 else {
894897 res_o = PyObject_GetItem (PyStackRef_AsPyObjectBorrow (container ), slice );
898+ // REGIONS: No barrier needed, since slice is local
899+ assert (PyRegion_IsLocal (slice ));
895900 Py_DECREF (slice );
896901 }
897902 PyStackRef_CLOSE (container );
@@ -909,6 +914,7 @@ dummy_func(
909914 }
910915
911916 op (_STORE_SLICE , (v , container , start , stop -- )) {
917+ // Regions: Write barrier called inside the function
912918 PyObject * slice = _PyBuildSlice_ConsumeRefs (PyStackRef_AsPyObjectSteal (start ),
913919 PyStackRef_AsPyObjectSteal (stop ));
914920 int err ;
@@ -917,6 +923,7 @@ dummy_func(
917923 }
918924 else {
919925 err = PyObject_SetItem (PyStackRef_AsPyObjectBorrow (container ), slice , PyStackRef_AsPyObjectBorrow (v ));
926+ assert (PyRegion_IsLocal (slice ));
920927 Py_DECREF (slice );
921928 }
922929 DECREF_INPUTS ();
@@ -1088,12 +1095,14 @@ dummy_func(
10881095 _PUSH_FRAME ;
10891096
10901097 inst (LIST_APPEND , (list , unused [oparg - 1 ], v -- list , unused [oparg - 1 ])) {
1098+ // REGIONS: The write barrier is called in the function
10911099 int err = _PyList_AppendTakeRef ((PyListObject * )PyStackRef_AsPyObjectBorrow (list ),
10921100 PyStackRef_AsPyObjectSteal (v ));
10931101 ERROR_IF (err < 0 );
10941102 }
10951103
10961104 inst (SET_ADD , (set , unused [oparg - 1 ], v -- set , unused [oparg - 1 ])) {
1105+ // REGIONS: The write barrier is called in the function
10971106 int err = _PySet_AddTakeRef ((PySetObject * )PyStackRef_AsPyObjectBorrow (set ),
10981107 PyStackRef_AsPyObjectSteal (v ));
10991108 ERROR_IF (err );
@@ -1138,6 +1147,7 @@ dummy_func(
11381147 // Ensure nonnegative, zero-or-one-digit ints.
11391148 DEOPT_IF (!_PyLong_IsNonNegativeCompact ((PyLongObject * )sub ));
11401149 Py_ssize_t index = ((PyLongObject * )sub )-> long_value .ob_digit [0 ];
1150+ DEOPT_IF (!PyRegion_IsLocal (list ));
11411151 DEOPT_IF (!LOCK_OBJECT (list ));
11421152 // Ensure index < len(list)
11431153 if (index >= PyList_GET_SIZE (list )) {
@@ -1154,6 +1164,7 @@ dummy_func(
11541164 PyStackRef_CLOSE_SPECIALIZED (sub_st , _PyLong_ExactDealloc );
11551165 DEAD (sub_st );
11561166 PyStackRef_CLOSE (list_st );
1167+ PyRegion_RemoveLocalRef (old_value );
11571168 Py_DECREF (old_value );
11581169 }
11591170
@@ -1165,6 +1176,7 @@ dummy_func(
11651176
11661177 assert (PyDict_CheckExact (dict ));
11671178 STAT_INC (STORE_SUBSCR , hit );
1179+ // REGIONS: The write barrier is called inside the function
11681180 int err = _PyDict_SetItem_Take2 ((PyDictObject * )dict ,
11691181 PyStackRef_AsPyObjectSteal (sub ),
11701182 PyStackRef_AsPyObjectSteal (value ));
@@ -1460,13 +1472,15 @@ dummy_func(
14601472
14611473 inst (POP_EXCEPT , (exc_value -- )) {
14621474 _PyErr_StackItem * exc_info = tstate -> exc_info ;
1475+ // Regions: `exc_info->exc_value` is local
14631476 Py_XSETREF (exc_info -> exc_value ,
14641477 PyStackRef_IsNone (exc_value )
14651478 ? NULL : PyStackRef_AsPyObjectSteal (exc_value ));
14661479 }
14671480
14681481 tier1 inst (RERAISE , (values [oparg ], exc_st -- values [oparg ])) {
14691482 PyObject * exc = PyStackRef_AsPyObjectSteal (exc_st );
1483+ assert (PyRegion_IsLocal (exc ));
14701484
14711485 assert (oparg >= 0 && oparg <= 2 );
14721486 if (oparg ) {
@@ -1605,8 +1619,10 @@ dummy_func(
16051619 }
16061620
16071621 op (_UNPACK_SEQUENCE , (seq -- unused [oparg ], top [0 ])) {
1622+ // Regions: seq_o is local, the LRC is increased by the steal
16081623 PyObject * seq_o = PyStackRef_AsPyObjectSteal (seq );
16091624 int res = _PyEval_UnpackIterableStackRef (tstate , seq_o , oparg , -1 , top );
1625+ PyRegion_RemoveLocalRef (seq_o );
16101626 Py_DECREF (seq_o );
16111627 ERROR_IF (res == 0 );
16121628 }
@@ -1663,8 +1679,10 @@ dummy_func(
16631679 }
16641680
16651681 inst (UNPACK_EX , (seq -- unused [oparg & 0xFF ], unused , unused [oparg >> 8 ], top [0 ])) {
1682+ // Regions: seq_o is local, the LRC is increased by the steal
16661683 PyObject * seq_o = PyStackRef_AsPyObjectSteal (seq );
16671684 int res = _PyEval_UnpackIterableStackRef (tstate , seq_o , oparg & 0xFF , oparg >> 8 , top );
1685+ PyRegion_AddLocalRef (seq_o );
16681686 Py_DECREF (seq_o );
16691687 ERROR_IF (res == 0 );
16701688 }
@@ -2640,16 +2658,29 @@ dummy_func(
26402658 ERROR_IF (true);
26412659 }
26422660 assert (_PyObject_GetManagedDict (owner_o ) == NULL );
2661+ PyObject * value_o = PyStackRef_AsPyObjectBorrow (value );
2662+ DEOPT_IF (!PyRegion_IsLocal (owner_o ) && !PyRegion_SameRegion (owner_o , value_o ));
2663+ // This call can't escape, due to the DEOPT_IF guard above.
2664+ // As is, it should only update the LRC and or be a noop. In
2665+ // the worst case, it should only may mark a region as dirty.
2666+ if (PyRegion_AddRef (owner_o , value_o )) {
2667+ UNLOCK_OBJECT (owner_o );
2668+ DECREF_INPUTS ();
2669+ ERROR_IF (true);
2670+ }
26432671 PyObject * * value_ptr = (PyObject * * )(((char * )owner_o ) + offset );
26442672 PyObject * old_value = * value_ptr ;
2645- FT_ATOMIC_STORE_PTR_RELEASE (* value_ptr , PyStackRef_AsPyObjectSteal ( value ));
2673+ FT_ATOMIC_STORE_PTR_RELEASE (* value_ptr , Py_NewRef ( value_o ));
26462674 if (old_value == NULL ) {
26472675 PyDictValues * values = _PyObject_InlineValues (owner_o );
26482676 Py_ssize_t index = value_ptr - values -> values ;
26492677 _PyDictValues_AddToInsertionOrder (values , index );
2678+ } else {
2679+ PyRegion_RemoveRef (owner_o , old_value );
26502680 }
26512681 UNLOCK_OBJECT (owner_o );
26522682 PyStackRef_CLOSE (owner );
2683+ PyStackRef_CLOSE (value );
26532684 Py_XDECREF (old_value );
26542685 }
26552686
@@ -2664,6 +2695,7 @@ dummy_func(
26642695 assert (Py_TYPE (owner_o )-> tp_flags & Py_TPFLAGS_MANAGED_DICT );
26652696 PyDictObject * dict = _PyObject_GetManagedDict (owner_o );
26662697 DEOPT_IF (dict == NULL );
2698+ DEOPT_IF (!PyRegion_IsLocal (dict ));
26672699 DEOPT_IF (!LOCK_OBJECT (dict ));
26682700 if (!Py_CHECKWRITE (owner_o ))
26692701 {
@@ -2709,6 +2741,7 @@ dummy_func(
27092741 op (_STORE_ATTR_SLOT , (index /1 , value , owner -- )) {
27102742 PyObject * owner_o = PyStackRef_AsPyObjectBorrow (owner );
27112743
2744+ DEOPT_IF (!PyRegion_IsLocal (owner_o ));
27122745 DEOPT_IF (!LOCK_OBJECT (owner_o ));
27132746 // TODO(Immutable) If the dictionary object has been made and is immutable, then this should fail,
27142747 // but we aren't finding the dictionary object here? Can we do this efficiently enough?
@@ -4989,9 +5022,19 @@ dummy_func(
49895022
49905023 inst (SET_FUNCTION_ATTRIBUTE , (attr_st , func_in -- func_out )) {
49915024 PyObject * func = PyStackRef_AsPyObjectBorrow (func_in );
5025+ if (!Py_CHECKWRITE (func ))
5026+ {
5027+ _PyEval_FormatExcNotWriteable (tstate , _PyFrame_GetCode (frame ), oparg );
5028+ DECREF_INPUTS ();
5029+ ERROR_IF (true);
5030+ }
49925031 PyObject * attr = PyStackRef_AsPyObjectSteal (attr_st );
49935032 func_out = func_in ;
49945033 DEAD (func_in );
5034+ if (PyRegion_AddRef (func , attr )) {
5035+ DECREF_INPUTS ();
5036+ ERROR_IF (true);
5037+ }
49955038 assert (PyFunction_Check (func ));
49965039 size_t offset = _Py_FunctionAttributeOffsets [oparg ];
49975040 assert (offset != 0 );
0 commit comments