[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:
parent
9e2407730f
commit
c8c176190a
@ -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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user