[compiler] Remove the optimized OSR code if deoptimizing at inside of loop

If the optimized code is deoptimized and the deoptimized exit offset is
inside of the optimized OSR code related loop, the optimized OSR code is
also out of date, remove the optimized OSR code, it will avoid hit the
optimized OSR code and soon later deoptimization of the optimized OSR
code.
This CL will reduce deoptimization. E.g. Deoptimization of JetStream2
case navier-stokes function addFields is reduced from twice to once.

Change-Id: I5bbf3039e916c3736b5b967d1f36b6ea90cfd40b
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3648219
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Commit-Queue: Tao Pan <tao.pan@intel.com>
Cr-Commit-Position: refs/heads/main@{#80826}
This commit is contained in:
Pan, Tao 2022-05-30 22:41:39 +08:00 committed by V8 LUCI CQ
parent 9e2407730f
commit c8c176190a

View File

@ -218,6 +218,92 @@ bool DeoptExitIsInsideOsrLoop(Isolate* isolate, JSFunction function,
UNREACHABLE();
}
bool TryGetOptimizedOsrCode(Isolate* isolate, FeedbackVector vector,
const interpreter::BytecodeArrayIterator& it,
CodeT* code_out) {
base::Optional<CodeT> maybe_code =
vector.GetOptimizedOsrCode(isolate, it.GetSlotOperand(2));
if (maybe_code.has_value()) {
*code_out = maybe_code.value();
return true;
}
return false;
}
// Deoptimize all osr'd loops which is in the same outermost loop with deopt
// exit. For example:
// for (;;) {
// for (;;) {
// } // Type a: loop start < OSR backedge < deopt exit
// for (;;) {
// <- Deopt
// for (;;) {
// } // Type b: deopt exit < loop start < OSR backedge
// } // Type c: loop start < deopt exit < OSR backedge
// } // The outermost loop
void DeoptAllOsrLoopsContainingDeoptExit(Isolate* isolate, JSFunction function,
BytecodeOffset deopt_exit_offset) {
DisallowGarbageCollection no_gc;
DCHECK(!deopt_exit_offset.IsNone());
if (!function.feedback_vector().maybe_has_optimized_osr_code()) return;
Handle<BytecodeArray> bytecode_array(
function.shared().GetBytecodeArray(isolate), isolate);
DCHECK(interpreter::BytecodeArrayIterator::IsValidOffset(
bytecode_array, deopt_exit_offset.ToInt()));
interpreter::BytecodeArrayIterator it(bytecode_array,
deopt_exit_offset.ToInt());
FeedbackVector vector = function.feedback_vector();
CodeT code;
base::SmallVector<CodeT, 8> osr_codes;
// Visit before the first loop-with-deopt is found
for (; !it.done(); it.Advance()) {
// We're only interested in loop ranges.
if (it.current_bytecode() != interpreter::Bytecode::kJumpLoop) continue;
// Is the deopt exit contained in the current loop?
if (base::IsInRange(deopt_exit_offset.ToInt(), it.GetJumpTargetOffset(),
it.current_offset())) {
break;
}
if (TryGetOptimizedOsrCode(isolate, vector, it, &code)) {
// Collect type b osr'd loops
osr_codes.push_back(code);
}
}
if (it.done()) return;
for (size_t i = 0, size = osr_codes.size(); i < size; i++) {
// Deoptimize type b osr'd loops
osr_codes[i].set_marked_for_deoptimization(true);
}
// Visit after the first loop-with-deopt is found
for (;; it.Advance()) {
// We're only interested in loop ranges.
if (it.current_bytecode() != interpreter::Bytecode::kJumpLoop) continue;
if (TryGetOptimizedOsrCode(isolate, vector, it, &code)) {
// Deoptimize type c osr'd loops
code.set_marked_for_deoptimization(true);
}
// We've reached nesting level 0, i.e. the current JumpLoop concludes a
// top-level loop.
const int loop_nesting_level = it.GetImmediateOperand(1);
if (loop_nesting_level == 0) break;
}
DCHECK(!it.done());
// Revisit from start of outermost loop to deopt
DCHECK_LE(it.GetJumpTargetOffset(), deopt_exit_offset.ToInt());
for (it.SetOffset(it.GetJumpTargetOffset());
it.current_offset() < deopt_exit_offset.ToInt(); it.Advance()) {
// We're only interested in loop ranges.
if (it.current_bytecode() != interpreter::Bytecode::kJumpLoop) continue;
if (TryGetOptimizedOsrCode(isolate, vector, it, &code)) {
// Deoptimize type a osr'd loops
code.set_marked_for_deoptimization(true);
}
}
}
} // namespace
RUNTIME_FUNCTION(Runtime_NotifyDeoptimized) {
@ -258,7 +344,10 @@ RUNTIME_FUNCTION(Runtime_NotifyDeoptimized) {
return ReadOnlyRoots(isolate).undefined_value();
}
// Non-OSR'd code is deoptimized unconditionally.
// Non-OSR'd code is deoptimized unconditionally. If the deoptimization occurs
// inside the outermost loop containning a loop that can trigger OSR
// compilation, we remove the OSR code, it will avoid hit the out of date OSR
// code and soon later deoptimization.
//
// For OSR'd code, we keep the optimized code around if deoptimization occurs
// outside the outermost loop containing the loop that triggered OSR
@ -267,9 +356,11 @@ RUNTIME_FUNCTION(Runtime_NotifyDeoptimized) {
// still worth jumping to the OSR'd code on the next run. The reduced cost of
// the loop should pay for the deoptimization costs.
const BytecodeOffset osr_offset = optimized_code->osr_offset();
if (osr_offset.IsNone() ||
DeoptExitIsInsideOsrLoop(isolate, *function, deopt_exit_offset,
osr_offset)) {
if (osr_offset.IsNone()) {
Deoptimizer::DeoptimizeFunction(*function, *optimized_code);
DeoptAllOsrLoopsContainingDeoptExit(isolate, *function, deopt_exit_offset);
} else if (DeoptExitIsInsideOsrLoop(isolate, *function, deopt_exit_offset,
osr_offset)) {
Deoptimizer::DeoptimizeFunction(*function, *optimized_code);
}