@@ -248,6 +248,61 @@ typenode_get_type(_Py_TYPENODE_t node)
248248 }
249249}
250250
251+ /**
252+ * @brief Gets the location of the node in the type context
253+ * @param ctx Pointer to type context to look in
254+ * @param node A node in type context `ctx`
255+ * @param is_localvar Pointer to boolean.
256+ If `node` is inside `ctx->type_locals`, this will return true
257+ * @return The index into the array the node is in.
258+ * If `*is_localvar` is true, the array is `ctx->type_locals`.
259+ * Otherwise it is `ctx->type_stack`
260+ */
261+ static int
262+ typenode_get_location (_PyTier2TypeContext * ctx , _Py_TYPENODE_t * node , bool * is_localvar )
263+ {
264+ // Search locals
265+ int nlocals = ctx -> type_locals_len ;
266+ int offset = (int )(node - ctx -> type_locals );
267+ if (offset >= 0 && offset < nlocals ) {
268+ * is_localvar = true;
269+ return offset ;
270+ }
271+
272+ // Search stack
273+ int nstack = ctx -> type_stack_len ;
274+ offset = (int )(node - ctx -> type_stack );
275+ for (int i = 0 ; i < nstack ; i ++ ) {
276+ * is_localvar = false;
277+ return offset ;
278+ }
279+
280+ Py_UNREACHABLE ();
281+ }
282+
283+ /**
284+ * @brief Check if two nodes in a type context are in the same tree
285+ * @param src
286+ */
287+ static bool typenode_is_same_tree (_Py_TYPENODE_t * x , _Py_TYPENODE_t * y )
288+ {
289+ _Py_TYPENODE_t * x_rootref = x ;
290+ _Py_TYPENODE_t * y_rootref = y ;
291+ uintptr_t x_tag = _Py_TYPENODE_GET_TAG (* x );
292+ uintptr_t y_tag = _Py_TYPENODE_GET_TAG (* y );
293+ switch (y_tag ) {
294+ case TYPE_REF : y_rootref = __typenode_get_rootptr (* y );
295+ case TYPE_ROOT : break ;
296+ default : Py_UNREACHABLE ();
297+ }
298+ switch (x_tag ) {
299+ case TYPE_REF : x_rootref = __typenode_get_rootptr (* x );
300+ case TYPE_ROOT : break ;
301+ default : Py_UNREACHABLE ();
302+ }
303+ return x_rootref == y_rootref ;
304+ }
305+
251306/**
252307 * @brief Performs TYPE_SET operation. dst tree becomes part of src tree
253308 *
@@ -279,55 +334,23 @@ __type_propagate_TYPE_SET(
279334 }
280335#endif
281336
282- if (!src_is_new ) {
283- // Check if they are the same tree
284- _Py_TYPENODE_t * srcrootref = src ;
285- _Py_TYPENODE_t * dstrootref = dst ;
286- uintptr_t dsttag = _Py_TYPENODE_GET_TAG (* dst );
287- uintptr_t srctag = _Py_TYPENODE_GET_TAG (* src );
288- switch (dsttag ) {
289- case TYPE_REF : dstrootref = __typenode_get_rootptr (* dst );
290- case TYPE_ROOT :
291- switch (srctag ) {
292- case TYPE_REF : srcrootref = __typenode_get_rootptr (* src );
293- case TYPE_ROOT :
294- if (srcrootref == dstrootref ) {
295- // Same tree, no point swapping
296- return ;
297- }
298- break ;
299- default :
300- Py_UNREACHABLE ();
301- }
302- break ;
303- default :
304- Py_UNREACHABLE ();
305- }
337+ if (!src_is_new && typenode_is_same_tree (src , dst )) {
338+ return ;
306339 }
307340
308341 uintptr_t tag = _Py_TYPENODE_GET_TAG (* dst );
342+ _Py_TYPENODE_t * rootref = dst ;
309343 switch (tag ) {
310- case TYPE_ROOT : {
344+ case TYPE_REF : rootref = __typenode_get_rootptr (* dst );
345+ case TYPE_ROOT :
311346 if (!src_is_new ) {
312347 // Make dst a reference to src
313- * dst = _Py_TYPENODE_MAKE_REF ((_Py_TYPENODE_t )src );
314- break ;
315- }
316- // Make dst the src
317- * dst = (_Py_TYPENODE_t )src ;
318- break ;
319- }
320- case TYPE_REF : {
321- _Py_TYPENODE_t * rootref = __typenode_get_rootptr (* dst );
322- if (!src_is_new ) {
323- // Traverse up to the root of dst, make root a reference to src
324348 * rootref = _Py_TYPENODE_MAKE_REF ((_Py_TYPENODE_t )src );
325349 break ;
326350 }
327- // Make root of dst the src
351+ // Make dst the src
328352 * rootref = (_Py_TYPENODE_t )src ;
329353 break ;
330- }
331354 default :
332355 Py_UNREACHABLE ();
333356 }
@@ -365,6 +388,10 @@ __type_propagate_TYPE_OVERWRITE(
365388 }
366389#endif
367390
391+ if (!src_is_new && typenode_is_same_tree (src , dst )) {
392+ return ;
393+ }
394+
368395 uintptr_t tag = _Py_TYPENODE_GET_TAG (* dst );
369396 switch (tag ) {
370397 case TYPE_ROOT : {
@@ -487,28 +514,8 @@ __type_propagate_TYPE_SWAP(
487514 _PyTier2TypeContext * type_context ,
488515 _Py_TYPENODE_t * src , _Py_TYPENODE_t * dst )
489516{
490- // Check if they are the same tree
491- _Py_TYPENODE_t * srcrootref = src ;
492- _Py_TYPENODE_t * dstrootref = dst ;
493- uintptr_t dsttag = _Py_TYPENODE_GET_TAG (* dst );
494- uintptr_t srctag = _Py_TYPENODE_GET_TAG (* src );
495- switch (dsttag ) {
496- case TYPE_REF : dstrootref = __typenode_get_rootptr (* dst );
497- case TYPE_ROOT :
498- switch (srctag ) {
499- case TYPE_REF : srcrootref = __typenode_get_rootptr (* src );
500- case TYPE_ROOT :
501- if (srcrootref == dstrootref ) {
502- // Same tree, no point swapping
503- return ;
504- }
505- break ;
506- default :
507- Py_UNREACHABLE ();
508- }
509- break ;
510- default :
511- Py_UNREACHABLE ();
517+ if (typenode_is_same_tree (src , dst )) {
518+ return ;
512519 }
513520
514521 // src and dst are different tree,
@@ -2658,6 +2665,93 @@ _PyTier2_GenerateNextBB(
26582665 return metadata -> tier2_start ;
26592666}
26602667
2668+ /**
2669+ * @brief Helper funnction of typecontext_is_compatible. See that for why we need this.
2670+ * @param ctx1 A pointer to a type context.
2671+ * @param ctx2 A pointer to a different type context.
2672+ * @param ctx1_node A pointer to type node belonging to ctx1.
2673+ * @param ctx2_node A pointer to type node belonging to ctx2t.
2674+ * @return If the type nodes' parent trees are compatible.
2675+ */
2676+ static bool
2677+ typenode_is_compatible (
2678+ _PyTier2TypeContext * ctx1 , _PyTier2TypeContext * ctx2 ,
2679+ _Py_TYPENODE_t * ctx1_node , _Py_TYPENODE_t * ctx2_node )
2680+ {
2681+ _Py_TYPENODE_t * root1 = ctx1_node ;
2682+ _Py_TYPENODE_t * root2 = ctx2_node ;
2683+ switch (_Py_TYPENODE_GET_TAG (* ctx1_node )) {
2684+ case TYPE_REF : root1 = __typenode_get_rootptr (* ctx1_node );
2685+ case TYPE_ROOT : break ;
2686+ default : Py_UNREACHABLE ();
2687+ }
2688+ switch (_Py_TYPENODE_GET_TAG (* ctx2_node )) {
2689+ case TYPE_REF : root2 = __typenode_get_rootptr (* ctx2_node );
2690+ case TYPE_ROOT : break ;
2691+ default : Py_UNREACHABLE ();
2692+ }
2693+
2694+ // Get location of each root
2695+ bool is_local1 , is_local2 ;
2696+ int node_idx1 = typenode_get_location (ctx1 , root1 , & is_local1 );
2697+ int node_idx2 = typenode_get_location (ctx2 , root2 , & is_local2 );
2698+
2699+ // Map each root to the corresponding location in the other tree
2700+ _Py_TYPENODE_t * mappedroot1 = is_local1
2701+ ? & ctx2 -> type_locals [node_idx1 ]
2702+ : & ctx2 -> type_stack [node_idx1 ];
2703+ _Py_TYPENODE_t * mappedroot2 = is_local2
2704+ ? & ctx1 -> type_locals [node_idx2 ]
2705+ : & ctx1 -> type_stack [node_idx2 ];
2706+
2707+ return typenode_is_same_tree (mappedroot1 , root2 )
2708+ && typenode_is_same_tree (mappedroot2 , root1 );
2709+ }
2710+
2711+ /**
2712+ * @brief Checks that type context 2 is compatible with context 1.
2713+ * ctx2 is compatible with ctx1 if any execution state with ctx2 can run on code emitted from ctx1
2714+ *
2715+ * @param ctx1 The base type context.
2716+ * @param ctx2 The type context to compare with.
2717+ * @return true if compatible, false otherwise.
2718+ */
2719+ static bool
2720+ typecontext_is_compatible (_PyTier2TypeContext * ctx1 , _PyTier2TypeContext * ctx2 )
2721+ {
2722+ // This function does two things:
2723+ // 1. Check that the trees are the same "shape" and equivalent. This allows
2724+ // ctx1's trees to be a subtree of ctx2.
2725+ // 2. Check that the trees resolve to the same root type.
2726+
2727+ #ifdef Py_DEBUG
2728+ // These should be true during runtime
2729+ assert (ctx1 -> type_locals_len == ctx2 -> type_locals_len );
2730+ assert (ctx1 -> type_stack_len == ctx2 -> type_stack_len );
2731+ int stack_elems1 = (int )(ctx1 -> type_stack_ptr - ctx1 -> type_stack );
2732+ int stack_elems2 = (int )(ctx2 -> type_stack_ptr - ctx2 -> type_stack );
2733+ assert (stack_elems1 == stack_elems2 );
2734+ #endif
2735+
2736+ // Check the locals
2737+ for (int i = 0 ; i < ctx1 -> type_locals_len ; i ++ ) {
2738+ if (!typenode_is_compatible (ctx1 , ctx2 , & ctx1 -> type_locals [i ],
2739+ & ctx2 -> type_locals [i ])) {
2740+ return false;
2741+ }
2742+ }
2743+
2744+ // Check the type stack
2745+ for (int i = 0 ; i < stack_elems1 ; i ++ ) {
2746+ if (!typenode_is_compatible (ctx1 , ctx2 , & ctx1 -> type_stack [i ],
2747+ & ctx2 -> type_stack [i ])) {
2748+ return false;
2749+ }
2750+ }
2751+
2752+ return true;
2753+ }
2754+
26612755/**
26622756 * @brief Calculates the difference between two type contexts.
26632757 * @param ctx1 The base type context.
@@ -2672,16 +2766,22 @@ diff_typecontext(_PyTier2TypeContext *ctx1, _PyTier2TypeContext *ctx2)
26722766 assert (ctx2 != NULL );
26732767#if BB_DEBUG
26742768 fprintf (stderr , " [*] Diffing type contexts\n" );
2769+ #if TYPEPROP_DEBUG
26752770 static void print_typestack (const _PyTier2TypeContext * type_context );
26762771 print_typestack (ctx1 );
26772772 print_typestack (ctx2 );
2773+ #endif
26782774#endif
26792775 assert (ctx1 -> type_locals_len == ctx2 -> type_locals_len );
26802776 assert (ctx1 -> type_stack_len == ctx2 -> type_stack_len );
26812777 int stack_elems1 = (int )(ctx1 -> type_stack_ptr - ctx1 -> type_stack );
26822778 int stack_elems2 = (int )(ctx2 -> type_stack_ptr - ctx2 -> type_stack );
26832779 assert (stack_elems1 == stack_elems2 );
26842780
2781+ if (!typecontext_is_compatible (ctx1 , ctx2 )) {
2782+ return INT_MAX ;
2783+ }
2784+
26852785 int diff = 0 ;
26862786 // Check the difference in the type locals
26872787 for (int i = 0 ; i < ctx1 -> type_locals_len ; i ++ ) {
@@ -2822,9 +2922,17 @@ _PyTier2_LocateJumpBackwardsBB(_PyInterpreterFrame *frame, uint16_t bb_id_tagged
28222922 assert (jump_offset_id >= 0 );
28232923 assert (candidate_bb_id >= 0 );
28242924 assert (candidate_bb_tier1_start != NULL );
2925+ #if BB_DEBUG
2926+ if (matching_bb_id != -1 ) {
2927+ fprintf (stderr , "Found jump target BB ID: %d\n" , matching_bb_id );
2928+ }
2929+ #endif
28252930 // We couldn't find a matching BB to jump to. Time to generate our own.
28262931 // This also requires rewriting our backwards jump to a forward jump later.
28272932 if (matching_bb_id == -1 ) {
2933+ #if BB_DEBUG
2934+ fprintf (stderr , "Generating new jump target BB ID: %d\n" , matching_bb_id );
2935+ #endif
28282936 // We should use the type context occuring at the end of the loop.
28292937 _PyTier2TypeContext * copied = _PyTier2TypeContext_Copy (curr_type_context );
28302938 if (copied == NULL ) {
@@ -2867,7 +2975,7 @@ _PyTier2_LocateJumpBackwardsBB(_PyInterpreterFrame *frame, uint16_t bb_id_tagged
28672975 assert (matching_bb_id >= 0 );
28682976 assert (matching_bb_id <= t2_info -> bb_data_curr );
28692977#if BB_DEBUG
2870- fprintf (stderr , "Found jump target BB ID: %d\n" , matching_bb_id );
2978+ fprintf (stderr , "Using jump target BB ID: %d\n" , matching_bb_id );
28712979#endif
28722980 _PyTier2BBMetadata * target_metadata = t2_info -> bb_data [matching_bb_id ];
28732981 return target_metadata -> tier2_start ;
0 commit comments