1// Copyright 2019 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/codegen/pending-optimization-table.h"
6
7#include "src/base/flags.h"
8#include "src/execution/isolate-inl.h"
9#include "src/heap/heap-inl.h"
10#include "src/objects/hash-table.h"
11#include "src/objects/js-objects.h"
12
13namespace v8 {
14namespace internal {
15
16enum class FunctionStatus : int {
17  kPrepareForOptimize = 1 << 0,
18  kMarkForOptimize = 1 << 1,
19  kAllowHeuristicOptimization = 1 << 2,
20};
21
22using FunctionStatusFlags = base::Flags<FunctionStatus>;
23
24void PendingOptimizationTable::PreparedForOptimization(
25    Isolate* isolate, Handle<JSFunction> function,
26    bool allow_heuristic_optimization) {
27  DCHECK(FLAG_testing_d8_test_runner);
28
29  FunctionStatusFlags status = FunctionStatus::kPrepareForOptimize;
30  if (allow_heuristic_optimization) {
31    status |= FunctionStatus::kAllowHeuristicOptimization;
32  }
33  Handle<SharedFunctionInfo> shared_info(function->shared(), isolate);
34
35  IsCompiledScope is_compiled_scope;
36  SharedFunctionInfo::EnsureBytecodeArrayAvailable(isolate, shared_info,
37                                                   &is_compiled_scope);
38
39  Handle<ObjectHashTable> table =
40      isolate->heap()->pending_optimize_for_test_bytecode().IsUndefined()
41          ? ObjectHashTable::New(isolate, 1)
42          : handle(ObjectHashTable::cast(
43                       isolate->heap()->pending_optimize_for_test_bytecode()),
44                   isolate);
45  Handle<Tuple2> tuple = isolate->factory()->NewTuple2(
46      handle(shared_info->GetBytecodeArray(isolate), isolate),
47      handle(Smi::FromInt(status), isolate), AllocationType::kYoung);
48  table =
49      ObjectHashTable::Put(table, handle(function->shared(), isolate), tuple);
50  isolate->heap()->SetPendingOptimizeForTestBytecode(*table);
51}
52
53bool PendingOptimizationTable::IsHeuristicOptimizationAllowed(
54    Isolate* isolate, JSFunction function) {
55  DCHECK(FLAG_testing_d8_test_runner);
56
57  Handle<Object> table =
58      handle(isolate->heap()->pending_optimize_for_test_bytecode(), isolate);
59  Handle<Object> entry =
60      table->IsUndefined()
61          ? handle(ReadOnlyRoots(isolate).the_hole_value(), isolate)
62          : handle(Handle<ObjectHashTable>::cast(table)->Lookup(
63                       handle(function.shared(), isolate)),
64                   isolate);
65  if (entry->IsTheHole()) {
66    return true;
67  }
68  DCHECK(entry->IsTuple2());
69  DCHECK(Handle<Tuple2>::cast(entry)->value2().IsSmi());
70  FunctionStatusFlags status(Smi::ToInt(Handle<Tuple2>::cast(entry)->value2()));
71  return status & FunctionStatus::kAllowHeuristicOptimization;
72}
73
74void PendingOptimizationTable::MarkedForOptimization(
75    Isolate* isolate, Handle<JSFunction> function) {
76  DCHECK(FLAG_testing_d8_test_runner);
77
78  Handle<Object> table =
79      handle(isolate->heap()->pending_optimize_for_test_bytecode(), isolate);
80  Handle<Object> entry =
81      table->IsUndefined()
82          ? handle(ReadOnlyRoots(isolate).the_hole_value(), isolate)
83          : handle(Handle<ObjectHashTable>::cast(table)->Lookup(
84                       handle(function->shared(), isolate)),
85                   isolate);
86  if (entry->IsTheHole()) {
87    PrintF("Error: Function ");
88    function->ShortPrint();
89    PrintF(
90        " should be prepared for optimization with "
91        "%%PrepareFunctionForOptimization before  "
92        "%%OptimizeFunctionOnNextCall / %%OptimizeOSR ");
93    UNREACHABLE();
94  }
95
96  DCHECK(entry->IsTuple2());
97  DCHECK(Handle<Tuple2>::cast(entry)->value2().IsSmi());
98  FunctionStatusFlags status(Smi::ToInt(Handle<Tuple2>::cast(entry)->value2()));
99  status = status.without(FunctionStatus::kPrepareForOptimize) |
100           FunctionStatus::kMarkForOptimize;
101  Handle<Tuple2>::cast(entry)->set_value2(Smi::FromInt(status));
102  table = ObjectHashTable::Put(Handle<ObjectHashTable>::cast(table),
103                               handle(function->shared(), isolate), entry);
104  isolate->heap()->SetPendingOptimizeForTestBytecode(*table);
105}
106
107void PendingOptimizationTable::FunctionWasOptimized(
108    Isolate* isolate, Handle<JSFunction> function) {
109  DCHECK(FLAG_testing_d8_test_runner);
110
111  if (isolate->heap()->pending_optimize_for_test_bytecode().IsUndefined()) {
112    return;
113  }
114
115  Handle<ObjectHashTable> table =
116      handle(ObjectHashTable::cast(
117                 isolate->heap()->pending_optimize_for_test_bytecode()),
118             isolate);
119  Handle<Object> value(table->Lookup(handle(function->shared(), isolate)),
120                       isolate);
121  // Remove only if we have already seen %OptimizeFunctionOnNextCall. If it is
122  // optimized for other reasons, still keep holding the bytecode since we may
123  // optimize it later.
124  if (!value->IsTheHole() &&
125      Smi::cast(Handle<Tuple2>::cast(value)->value2()).value() ==
126          static_cast<int>(FunctionStatus::kMarkForOptimize)) {
127    bool was_present;
128    table = table->Remove(isolate, table, handle(function->shared(), isolate),
129                          &was_present);
130    DCHECK(was_present);
131    isolate->heap()->SetPendingOptimizeForTestBytecode(*table);
132  }
133}
134
135}  // namespace internal
136}  // namespace v8
137