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

This is a reland of commit c8c176190a

This CL includes:

- crrev.com/c/3679846 Add condition use_ic to the removing the optimized OSR code logic
- crrev.com/c/3686589 Add out of bytecode array to break condition of removing OSR code cache logic
- Add JumpLoop nesting level 0 to break condition of removing OSR code cache logic
- Change to use Deoptimizer::DeoptimizeFunction() to deoptimize OSR code

Original change's description:
> [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}

Bug: chromium:1330444
Change-Id: I97a466ddfa764438b45f33c6ae33cb921d57278d
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3690451
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Commit-Queue: Tao Pan <tao.pan@intel.com>
Cr-Commit-Position: refs/heads/main@{#81110}
This commit is contained in:
Pan, Tao 2022-06-13 17:05:43 +08:00 committed by V8 LUCI CQ
parent c2ede7acd2
commit c2d239ddb3
2 changed files with 106 additions and 4 deletions

View File

@ -218,6 +218,102 @@ 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 (!FLAG_use_ic ||
!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;
}
// We've reached nesting level 0, i.e. the current JumpLoop concludes a
// top-level loop, return as the deopt exit is not in any loop. For example:
// <- Deopt
// for (;;) {
// } // The outermost loop
const int loop_nesting_level = it.GetImmediateOperand(1);
if (loop_nesting_level == 0) return;
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
Deoptimizer::DeoptimizeFunction(function, FromCodeT(osr_codes[i]));
}
// Visit after 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;
if (TryGetOptimizedOsrCode(isolate, vector, it, &code)) {
// Deoptimize type c osr'd loops
Deoptimizer::DeoptimizeFunction(function, FromCodeT(code));
}
// 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;
}
if (it.done()) return;
// 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
Deoptimizer::DeoptimizeFunction(function, FromCodeT(code));
}
}
}
} // namespace
RUNTIME_FUNCTION(Runtime_NotifyDeoptimized) {
@ -258,7 +354,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 +366,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);
}

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --interrupt-budget=100 --deopt-every-n-times=4
// Flags: --allow-natives-syntax --use-osr
"use strict";