[testing] Prevent heuristics from triggering optimization in tests

This CL adds a mechanism that prevents the RuntimeProfiler from
triggering optimization of a function after
%PrepareFunctionForOptimization has been called. This is useful to
prevent flakiness in tests, as sometimes a function that already
got deoptimized would receive a new code object from a concurrent
compile that was triggered by a heuristic just in the right moment
for the assertUnoptimized test to fail. For example, the following
was happening:

PrepareFunctionForOptimization
[marking `testAdd` for optimized recompilation, reason: small function]
[concurrently compiling method `testAdd` using TurboFan]
[manually marking `testAdd` for non-concurrent optimization]
[synchonously compiling method `testAdd` using TurboFan]
[synchonously optimizing `testAdd` produced code object 0xAAAA - took 1.638 ms]
Runtime_GetOptimizationStatus OPTIMIZED `testAdd` (code object 0xAAAA)
DeoptimizeFunction `testAdd` with Code Object 0xAAAA
[concurrently optimizing `testAdd` produced code object 0xBBBB - took 3.377 ms]
Runtime_GetOptimizationStatus OPTIMIZED `testAdd` (code object 0xBBBB)

Bug: v8:9563
Change-Id: Ia4c846aba95281589317d43b82383e70fe0a35f5
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1763546
Reviewed-by: Ross McIlroy <rmcilroy@chromium.org>
Reviewed-by: Yang Guo <yangguo@chromium.org>
Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63343}
This commit is contained in:
Sigurd Schneider 2019-08-22 13:22:40 +02:00 committed by Commit Bot
parent 6fa5de0b8e
commit 6d9b7988f7
6 changed files with 79 additions and 13 deletions

View File

@ -4,6 +4,7 @@
#include "src/codegen/pending-optimization-table.h"
#include "src/base/flags.h"
#include "src/execution/isolate-inl.h"
#include "src/heap/heap-inl.h"
#include "src/objects/hash-table.h"
@ -12,12 +13,24 @@
namespace v8 {
namespace internal {
enum class FunctionStatus { kPrepareForOptimize, kMarkForOptimize };
enum class FunctionStatus : int {
kPrepareForOptimize = 1 << 0,
kMarkForOptimize = 1 << 1,
kAllowHeuristicOptimization = 1 << 2,
};
using FunctionStatusFlags = base::Flags<FunctionStatus>;
void PendingOptimizationTable::PreparedForOptimization(
Isolate* isolate, Handle<JSFunction> function) {
Isolate* isolate, Handle<JSFunction> function,
bool allow_heuristic_optimization) {
DCHECK(FLAG_testing_d8_test_runner);
FunctionStatusFlags status = FunctionStatus::kPrepareForOptimize;
if (allow_heuristic_optimization) {
status |= FunctionStatus::kAllowHeuristicOptimization;
}
Handle<ObjectHashTable> table =
isolate->heap()->pending_optimize_for_test_bytecode().IsUndefined()
? ObjectHashTable::New(isolate, 1)
@ -26,15 +39,33 @@ void PendingOptimizationTable::PreparedForOptimization(
isolate);
Handle<Tuple2> tuple = isolate->factory()->NewTuple2(
handle(function->shared().GetBytecodeArray(), isolate),
handle(
Smi::FromInt(static_cast<int>(FunctionStatus::kPrepareForOptimize)),
isolate),
AllocationType::kYoung);
handle(Smi::FromInt(status), isolate), AllocationType::kYoung);
table =
ObjectHashTable::Put(table, handle(function->shared(), isolate), tuple);
isolate->heap()->SetPendingOptimizeForTestBytecode(*table);
}
bool PendingOptimizationTable::IsHeuristicOptimizationAllowed(
Isolate* isolate, JSFunction function) {
DCHECK(FLAG_testing_d8_test_runner);
Handle<Object> table =
handle(isolate->heap()->pending_optimize_for_test_bytecode(), isolate);
Handle<Object> entry =
table->IsUndefined()
? handle(ReadOnlyRoots(isolate).the_hole_value(), isolate)
: handle(Handle<ObjectHashTable>::cast(table)->Lookup(
handle(function.shared(), isolate)),
isolate);
if (entry->IsTheHole()) {
return true;
}
DCHECK(entry->IsTuple2());
DCHECK(Handle<Tuple2>::cast(entry)->value2().IsSmi());
FunctionStatusFlags status(Smi::ToInt(Handle<Tuple2>::cast(entry)->value2()));
return status & FunctionStatus::kAllowHeuristicOptimization;
}
void PendingOptimizationTable::MarkedForOptimization(
Isolate* isolate, Handle<JSFunction> function) {
DCHECK(FLAG_testing_d8_test_runner);
@ -58,8 +89,11 @@ void PendingOptimizationTable::MarkedForOptimization(
}
DCHECK(entry->IsTuple2());
Handle<Tuple2>::cast(entry)->set_value2(
Smi::FromInt(static_cast<int>(FunctionStatus::kMarkForOptimize)));
DCHECK(Handle<Tuple2>::cast(entry)->value2().IsSmi());
FunctionStatusFlags status(Smi::ToInt(Handle<Tuple2>::cast(entry)->value2()));
status = status.without(FunctionStatus::kPrepareForOptimize) |
FunctionStatus::kMarkForOptimize;
Handle<Tuple2>::cast(entry)->set_value2(Smi::FromInt(status));
table = ObjectHashTable::Put(Handle<ObjectHashTable>::cast(table),
handle(function->shared(), isolate), entry);
isolate->heap()->SetPendingOptimizeForTestBytecode(*table);

View File

@ -21,7 +21,8 @@ class PendingOptimizationTable {
// strongly in pending optimization table preventing the bytecode to be
// flushed.
static void PreparedForOptimization(Isolate* isolate,
Handle<JSFunction> function);
Handle<JSFunction> function,
bool allow_heuristic_optimization);
// This function should be called when the function is marked for optimization
// via the intrinsics. This will update the state of the bytecode array in the
@ -36,6 +37,12 @@ class PendingOptimizationTable {
// then this function removes the entry from pending optimization table.
static void FunctionWasOptimized(Isolate* isolate,
Handle<JSFunction> function);
// This function returns whether a heuristic is allowed to trigger
// optimization the function. This mechanism is used in tests to prevent
// heuristics from interfering with manually triggered optimization.
static bool IsHeuristicOptimizationAllowed(Isolate* isolate,
JSFunction function);
};
} // namespace internal

View File

@ -8,6 +8,7 @@
#include "src/codegen/assembler.h"
#include "src/codegen/compilation-cache.h"
#include "src/codegen/compiler.h"
#include "src/codegen/pending-optimization-table.h"
#include "src/execution/execution.h"
#include "src/execution/frames-inl.h"
#include "src/handles/global-handles.h"
@ -119,6 +120,17 @@ void RuntimeProfiler::MaybeOptimize(JSFunction function,
}
return;
}
if (FLAG_testing_d8_test_runner) {
if (!PendingOptimizationTable::IsHeuristicOptimizationAllowed(isolate_,
function)) {
if (FLAG_trace_opt_verbose) {
PrintF("[function ");
function.PrintName();
PrintF(" has been marked manually for optimization]\n");
}
return;
}
}
if (FLAG_always_osr) {
AttemptOnStackReplacement(frame, AbstractCode::kMaxLoopNestingMarker);

View File

@ -349,12 +349,24 @@ RUNTIME_FUNCTION(Runtime_EnsureFeedbackVectorForFunction) {
RUNTIME_FUNCTION(Runtime_PrepareFunctionForOptimization) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
DCHECK(args.length() == 1 || args.length() == 2);
if (!args[0].IsJSFunction()) {
return ReadOnlyRoots(isolate).undefined_value();
}
CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
bool allow_heuristic_optimization = false;
if (args.length() == 2) {
CONVERT_ARG_HANDLE_CHECKED(Object, sync_object, 1);
if (!sync_object->IsString())
return ReadOnlyRoots(isolate).undefined_value();
Handle<String> sync = Handle<String>::cast(sync_object);
if (sync->IsOneByteEqualTo(
StaticCharVector("allow heuristic optimization"))) {
allow_heuristic_optimization = true;
}
}
if (!EnsureFeedbackVector(function)) {
return ReadOnlyRoots(isolate).undefined_value();
}
@ -375,7 +387,8 @@ RUNTIME_FUNCTION(Runtime_PrepareFunctionForOptimization) {
// Hold onto the bytecode array between marking and optimization to ensure
// it's not flushed.
if (FLAG_testing_d8_test_runner) {
PendingOptimizationTable::PreparedForOptimization(isolate, function);
PendingOptimizationTable::PreparedForOptimization(
isolate, function, allow_heuristic_optimization);
}
return ReadOnlyRoots(isolate).undefined_value();

View File

@ -502,7 +502,7 @@ namespace internal {
F(NotifyContextDisposed, 0, 1) \
F(OptimizeFunctionOnNextCall, -1, 1) \
F(OptimizeOsr, -1, 1) \
F(PrepareFunctionForOptimization, 1, 1) \
F(PrepareFunctionForOptimization, -1, 1) \
F(PrintWithNameForAssert, 2, 1) \
F(RedirectToWasmInterpreter, 2, 1) \
F(RunningInSimulator, 0, 1) \

View File

@ -12,7 +12,7 @@ function f() {
return s;
}
%PrepareFunctionForOptimization(f);
%PrepareFunctionForOptimization(f, "allow heuristic optimization");
f();
f();
f();