Skip to content

Commit 8cba63a

Browse files
committed
Improved documentation for is_borrow_safe and check_borrow_safety_globally
1 parent a4300a5 commit 8cba63a

File tree

1 file changed

+25
-53
lines changed

1 file changed

+25
-53
lines changed

Python/flowgraph.c

Lines changed: 25 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2708,7 +2708,7 @@ load_fast_push_block(basicblock ***sp, basicblock *target,
27082708
/*
27092709
* Recursively determine if a borrowed reference remains safe along all CFG paths.
27102710
*
2711-
* This function performs a Depth-First Search (DFS) starting from 'current_block'.
2711+
* This function performs a DFS starting from 'current_block'.
27122712
* It tracks a specific value that was notionally loaded by a LOAD_FAST_BORROW
27132713
* instruction and is currently at 'depth_of_value_on_entry' on the operand stack
27142714
* (0 being TOS, -1 if already known to be consumed). The 'original_local_idx'
@@ -2733,20 +2733,11 @@ load_fast_push_block(basicblock ***sp, basicblock *target,
27332733
* - 'depth_of_value_on_entry': The depth of the tracked item upon entering 'current_block'.
27342734
* - 'current_target_depth': The depth of the item as instructions in 'current_block' are processed.
27352735
* - 'depth_before_jump_op_effect': When a jump instruction is encountered, this
2736-
* is calculated by simulating all prior instructions in 'current_block' to find
2737-
* the item's depth *just before* the jump instruction itself has any stack effect.
2738-
* This precise depth is then used to calculate the 'target_entry_depth' for the
2739-
* recursive call to the jump's target block.
2736+
* is calculated by simulating all prior instructions in 'current_block' to find
2737+
* the item's depth *just before* the jump instruction itself has any stack effect.
2738+
* This precise depth is then used to calculate the 'target_entry_depth' for the
2739+
* recursive call to the jump's target block.
27402740
*
2741-
* Args:
2742-
* current_block: The basic block to start analysis from for this recursive step.
2743-
* depth_of_value_on_entry: The depth of the tracked borrowed value from TOS
2744-
* (0 = TOS, -1 if already consumed).
2745-
* original_local_idx: The index of the local variable backing the borrow.
2746-
* g: The CFG builder.
2747-
*
2748-
* Returns:
2749-
* true if the borrow is safe along all paths from this point, false otherwise.
27502741
*/
27512742
static bool
27522743
is_borrow_safe(
@@ -2771,40 +2762,34 @@ is_borrow_safe(
27712762
int opcode = instr->i_opcode;
27722763
int oparg = instr->i_oparg;
27732764

2774-
// 1. Check if the original local is killed before the value is consumed.
27752765
if ((opcode == STORE_FAST || opcode == DELETE_FAST || opcode == LOAD_FAST_AND_CLEAR) &&
27762766
oparg == original_local_idx) {
27772767
return false;
27782768
}
27792769

2780-
// 2. Simulate stack effect and check for consumption or unsafe store.
27812770
stack_effects effects_noj;
27822771
if (get_stack_effects(opcode, oparg, 0, &effects_noj) < 0) {
2783-
return false; // Error in stack effect calculation
2772+
return false;
27842773
}
27852774
int num_popped = _PyOpcode_num_popped(opcode, oparg);
27862775
int num_pushed = _PyOpcode_num_pushed(opcode, oparg);
27872776

27882777
if (current_target_depth < num_popped) {
27892778
if (opcode == STORE_FAST && current_target_depth == 0) {
2790-
// Unsafe: borrowed value was at TOS and is being stored into a local.
27912779
return false;
27922780
}
2793-
return true; // Safely consumed by this instruction.
2781+
return true;
27942782
}
2795-
// Value not consumed, update its depth.
27962783
current_target_depth = current_target_depth - num_popped + num_pushed;
27972784

2798-
// 3. Handle branches (jumps are always last in a basic block).
27992785
if (HAS_TARGET(opcode)) {
28002786
if (i != current_block->b_iused - 1) {
28012787
continue; // Skip jumps that are not the last instruction in the block.
28022788
}
28032789
bool safe_on_all_branches = true;
28042790

2805-
// Calculate item's depth just before this jump instruction's own stack effect.
28062791
Py_ssize_t depth_before_jump_op_effect = depth_of_value_on_entry;
2807-
for(int k=0; k < i; ++k) { // Iterate instructions before the current jump
2792+
for(int k=0; k < i; ++k) {
28082793
cfg_instr *prev_instr_in_block = &current_block->b_instr[k];
28092794
// Kill check for intermediate instructions
28102795
if ((prev_instr_in_block->i_opcode == STORE_FAST ||
@@ -2818,11 +2803,11 @@ is_borrow_safe(
28182803
return false;
28192804
}
28202805
int prev_popped_val = _PyOpcode_num_popped(prev_instr_in_block->i_opcode, prev_instr_in_block->i_oparg);
2821-
if (depth_before_jump_op_effect < prev_popped_val) { // Consumed before jump
2806+
if (depth_before_jump_op_effect < prev_popped_val) {
28222807
if (prev_instr_in_block->i_opcode == STORE_FAST && depth_before_jump_op_effect == 0) {
2823-
return false; // Stored into local
2808+
return false;
28242809
}
2825-
return true; // Safely consumed before the jump
2810+
return true;
28262811
}
28272812
depth_before_jump_op_effect = depth_before_jump_op_effect - prev_popped_val +
28282813
_PyOpcode_num_pushed(prev_instr_in_block->i_opcode, prev_instr_in_block->i_oparg);
@@ -2834,11 +2819,10 @@ is_borrow_safe(
28342819
int jump_popped_val = _PyOpcode_num_popped(opcode, oparg);
28352820
Py_ssize_t target_entry_depth = depth_before_jump_op_effect;
28362821

2837-
if (target_entry_depth < jump_popped_val) { // Consumed by the jump op itself
2822+
if (target_entry_depth < jump_popped_val) {
28382823
if (opcode == STORE_FAST && target_entry_depth == 0) {
28392824
safe_on_all_branches = false;
28402825
}
2841-
// else: safely consumed by jump operation.
28422826
} else { // Not consumed by jump op, recurse on target.
28432827
target_entry_depth = target_entry_depth - jump_popped_val + _PyOpcode_num_pushed(opcode, oparg);
28442828
if (!is_borrow_safe(instr->i_target, target_entry_depth, original_local_idx, g)) {
@@ -2848,7 +2832,6 @@ is_borrow_safe(
28482832

28492833
// Analyze fallthrough path for conditional jumps, if the jump path was safe.
28502834
if (safe_on_all_branches && IS_CONDITIONAL_JUMP_OPCODE(opcode) && current_block->b_next) {
2851-
// 'current_target_depth' already reflects the stack state if the jump is *not* taken.
28522835
if (!is_borrow_safe(current_block->b_next, current_target_depth, original_local_idx, g)) {
28532836
safe_on_all_branches = false;
28542837
}
@@ -2862,14 +2845,16 @@ is_borrow_safe(
28622845
return is_borrow_safe(current_block->b_next, current_target_depth, original_local_idx, g);
28632846
}
28642847

2865-
// No further path (or no fallthrough) and value is still on stack (current_target_depth is valid).
2866-
// This means it's unconsumed at the end of a CFG path.
2848+
/*
2849+
No further path (or no fallthrough) and value is still on stack (current_target_depth is valid).
2850+
This means it's unconsumed at the end of a CFG path, so it's unsafe.
2851+
*/
28672852
return false;
28682853
}
28692854

28702855

28712856
/*
2872-
* Initiates a Control Flow Graph (CFG) wide safety check for a borrowed reference.
2857+
* Initiates a CFG wide safety check for a borrowed reference.
28732858
*
28742859
* This function is called when a LOAD_FAST instruction is a candidate for
28752860
* LOAD_FAST_BORROW, but its produced value ('REF_UNCONSUMED' flag is set)
@@ -2888,17 +2873,6 @@ is_borrow_safe(
28882873
* to ensure that 'is_borrow_safe()' uses a fresh set of visited markers
28892874
* ('b_dfs_visited_gen') for its analysis.
28902875
*
2891-
* Args:
2892-
* producer_block: The basic block containing the LOAD_FAST candidate.
2893-
* depth_of_value_at_producer_end: The depth of the candidate borrowed value
2894-
* from TOS at the end of 'producer_block'.
2895-
* (0 = TOS, -1 if already consumed - though
2896-
* this function expects a live value).
2897-
* original_local_idx: The index of the local variable backing the borrow.
2898-
* g: The CFG builder.
2899-
*
2900-
* Returns:
2901-
* true if the borrow is safe across all subsequent CFG paths, false otherwise.
29022876
*/
29032877
static bool
29042878
check_borrow_safety_globally(
@@ -2923,11 +2897,10 @@ check_borrow_safety_globally(
29232897
int jump_popped = _PyOpcode_num_popped(last_instr->i_opcode, last_instr->i_oparg);
29242898
Py_ssize_t entry_depth_for_target = depth_of_value_at_producer_end;
29252899

2926-
if (entry_depth_for_target < jump_popped) { // Consumed by the jump itself.
2900+
if (entry_depth_for_target < jump_popped) {
29272901
if (last_instr->i_opcode == STORE_FAST && entry_depth_for_target == 0) {
2928-
overall_safety = false; // Unsafe store of borrowed value.
2902+
overall_safety = false;
29292903
}
2930-
// else: safely consumed.
29312904
} else {
29322905
entry_depth_for_target = entry_depth_for_target - jump_popped +
29332906
_PyOpcode_num_pushed(last_instr->i_opcode, last_instr->i_oparg);
@@ -2938,17 +2911,16 @@ check_borrow_safety_globally(
29382911

29392912
// Analyze fallthrough path for conditional jumps, if the jump path was safe.
29402913
if (overall_safety && IS_CONDITIONAL_JUMP_OPCODE(last_instr->i_opcode) && producer_block->b_next) {
2941-
stack_effects effects_fall; // Stack effect if jump is NOT taken.
2914+
stack_effects effects_fall;
29422915
if (get_stack_effects(last_instr->i_opcode, last_instr->i_oparg, 0, &effects_fall) < 0) return false;
29432916
int fall_popped = _PyOpcode_num_popped(last_instr->i_opcode, last_instr->i_oparg);
29442917
Py_ssize_t entry_depth_for_fallthrough = depth_of_value_at_producer_end;
29452918

2946-
if (entry_depth_for_fallthrough < fall_popped) { // Consumed by not taking jump.
2919+
if (entry_depth_for_fallthrough < fall_popped) {
29472920
if (last_instr->i_opcode == STORE_FAST && entry_depth_for_fallthrough == 0) {
2948-
overall_safety = false; // Unsafe store.
2921+
overall_safety = false;
29492922
}
2950-
// else: safely consumed.
2951-
} else { // Not consumed by not taking jump, recurse on fallthrough.
2923+
} else { // Recurse on fallthrough.
29522924
entry_depth_for_fallthrough = entry_depth_for_fallthrough - fall_popped +
29532925
_PyOpcode_num_pushed(last_instr->i_opcode, last_instr->i_oparg);
29542926
if (!is_borrow_safe(producer_block->b_next, entry_depth_for_fallthrough, original_local_idx, g)) {
@@ -2960,8 +2932,8 @@ check_borrow_safety_globally(
29602932
if (!is_borrow_safe(producer_block->b_next, depth_of_value_at_producer_end, original_local_idx, g)) {
29612933
overall_safety = false;
29622934
}
2963-
} else { // No successors (e.g., block ends in RETURN/RAISE) and value still on stack.
2964-
overall_safety = false; // Unconsumed at end of a CFG path.
2935+
} else {
2936+
overall_safety = false;
29652937
}
29662938
return overall_safety;
29672939
}

0 commit comments

Comments
 (0)