Skip to content

Commit c75d91c

Browse files
Don't limit control-flow exits
1 parent 0e92118 commit c75d91c

File tree

4 files changed

+19
-10
lines changed

4 files changed

+19
-10
lines changed

Include/internal/pycore_optimizer.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ typedef struct {
3737
typedef struct _PyExitData {
3838
uint32_t target;
3939
uint16_t index;
40-
char is_dynamic;
40+
char is_dynamic:4;
41+
char is_control_flow:4;
4142
_Py_BackoffCounter temperature;
4243
struct _PyExecutorObject *executor;
4344
} _PyExitData;

Python/bytecodes.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5276,10 +5276,10 @@ dummy_func(
52765276
if (frame->lltrace >= 2) {
52775277
printf("SIDE EXIT: [UOp ");
52785278
_PyUOpPrint(&next_uop[-1]);
5279-
printf(", exit %tu, temp %d, target %d -> %s]\n",
5279+
printf(", exit %tu, temp %d, target %d -> %s, is_control_flow %d]\n",
52805280
exit - current_executor->exits, exit->temperature.value_and_backoff,
52815281
(int)(target - _PyFrame_GetBytecode(frame)),
5282-
_PyOpcode_OpName[target->op.code]);
5282+
_PyOpcode_OpName[target->op.code], exit->is_control_flow);
52835283
}
52845284
#endif
52855285
tstate->jit_exit = exit;
@@ -5486,7 +5486,9 @@ dummy_func(
54865486
}
54875487
_PyExecutorObject *previous_executor = _PyExecutor_FromExit(exit);
54885488
assert(tstate->current_executor == (PyObject *)previous_executor);
5489-
int chain_depth = previous_executor->vm_data.chain_depth + 1;
5489+
// For control-flow guards, we don't want to increase the chain depth, as those don't actually
5490+
// represent deopts but rather just normal programs!
5491+
int chain_depth = previous_executor->vm_data.chain_depth + !exit->is_control_flow;
54905492
// Note: it's safe to use target->op.arg here instead of the oparg given by EXTENDED_ARG.
54915493
// The invariant in the optimizer is the deopt target always points back to the first EXTENDED_ARG.
54925494
// So setting it to anything else is wrong.

Python/executor_cases.c.h

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/optimizer.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,7 @@ is_for_iter_test[MAX_UOP_ID + 1] = {
470470
[_GUARD_NOT_EXHAUSTED_RANGE] = 1,
471471
[_GUARD_NOT_EXHAUSTED_LIST] = 1,
472472
[_GUARD_NOT_EXHAUSTED_TUPLE] = 1,
473+
[_FOR_ITER_TIER_TWO] = 1,
473474
};
474475

475476
static const uint16_t
@@ -757,6 +758,7 @@ _PyJit_translate_single_bytecode_to_trace(
757758
// inner loop might start and let the traces rejoin.
758759
OPT_STAT_INC(inner_loop);
759760
ADD_TO_TRACE(_EXIT_TRACE, 0, 0, target);
761+
trace[trace_length-1].operand1 = true; // is_control_flow
760762
DPRINTF(2, "JUMP_BACKWARD not to top ends trace %p %p %p\n", next_instr, tstate->interp->jit_state.close_loop_instr, tstate->interp->jit_state.insert_exec_instr);
761763
goto done;
762764
}
@@ -933,6 +935,7 @@ _PyJit_translate_single_bytecode_to_trace(
933935
// We previously reversed one.
934936
max_length += 1;
935937
ADD_TO_TRACE(_EXIT_TRACE, 0, 0, target);
938+
trace[trace_length-1].operand1 = true; // is_control_flow
936939
}
937940
tstate->interp->jit_state.code_curr_size = trace_length;
938941
tstate->interp->jit_state.code_max_size = max_length;
@@ -1033,13 +1036,14 @@ count_exits(_PyUOpInstruction *buffer, int length)
10331036
return exit_count;
10341037
}
10351038

1036-
static void make_exit(_PyUOpInstruction *inst, int opcode, int target)
1039+
static void make_exit(_PyUOpInstruction *inst, int opcode, int target, bool is_control_flow)
10371040
{
10381041
inst->opcode = opcode;
10391042
inst->oparg = 0;
10401043
inst->operand0 = 0;
10411044
inst->format = UOP_FORMAT_TARGET;
10421045
inst->target = target;
1046+
inst->operand1 = is_control_flow;
10431047
#ifdef Py_STATS
10441048
inst->execution_count = 0;
10451049
#endif
@@ -1096,8 +1100,9 @@ prepare_for_execution(_PyUOpInstruction *buffer, int length)
10961100
exit_op = _DYNAMIC_EXIT;
10971101
unique_target = true;
10981102
}
1103+
bool is_control_flow = (opcode == _GUARD_IS_FALSE_POP || opcode == _GUARD_IS_TRUE_POP || is_for_iter_test[opcode]);
10991104
if (unique_target || jump_target != current_jump_target || current_exit_op != exit_op) {
1100-
make_exit(&buffer[next_spare], exit_op, jump_target);
1105+
make_exit(&buffer[next_spare], exit_op, jump_target, is_control_flow);
11011106
current_exit_op = exit_op;
11021107
current_jump_target = jump_target;
11031108
current_jump = next_spare;
@@ -1113,7 +1118,7 @@ prepare_for_execution(_PyUOpInstruction *buffer, int length)
11131118
current_popped = popped;
11141119
current_error = next_spare;
11151120
current_error_target = target;
1116-
make_exit(&buffer[next_spare], _ERROR_POP_N, 0);
1121+
make_exit(&buffer[next_spare], _ERROR_POP_N, 0, false);
11171122
buffer[next_spare].operand0 = target;
11181123
next_spare++;
11191124
}
@@ -1250,6 +1255,7 @@ make_executor_from_uops(_PyUOpInstruction *buffer, int length, const _PyBloomFil
12501255
dest->operand0 = (uint64_t)exit;
12511256
exit->executor = opcode == _EXIT_TRACE ? cold : cold_dynamic;
12521257
exit->is_dynamic = (char)(opcode == _DYNAMIC_EXIT);
1258+
exit->is_control_flow = (char)buffer[i].operand1;
12531259
next_exit--;
12541260
}
12551261
}

0 commit comments

Comments
 (0)