interpreter.cc 14.7 KB
Newer Older
1 2 3 4 5 6
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/interpreter/interpreter.h"

7
#include <fstream>
8
#include <memory>
9

10
#include "builtins-generated/bytecodes-builtins-list.h"
11
#include "src/ast/prettyprinter.h"
12
#include "src/ast/scopes.h"
13 14
#include "src/codegen/compiler.h"
#include "src/codegen/unoptimized-compilation-info.h"
15 16
#include "src/init/bootstrapper.h"
#include "src/init/setup-isolate.h"
17
#include "src/interpreter/bytecode-generator.h"
18
#include "src/interpreter/bytecodes.h"
19
#include "src/logging/counters-inl.h"
20
#include "src/objects/objects-inl.h"
21
#include "src/objects/shared-function-info.h"
22
#include "src/objects/slots.h"
23
#include "src/objects/visitors.h"
24
#include "src/parsing/parse-info.h"
25
#include "src/snapshot/snapshot.h"
26
#include "src/utils/ostreams.h"
27 28 29 30 31

namespace v8 {
namespace internal {
namespace interpreter {

32
class InterpreterCompilationJob final : public UnoptimizedCompilationJob {
33
 public:
34 35 36 37
  InterpreterCompilationJob(
      ParseInfo* parse_info, FunctionLiteral* literal,
      AccountingAllocator* allocator,
      std::vector<FunctionLiteral*>* eager_inner_literals);
38 39 40

 protected:
  Status ExecuteJobImpl() final;
41 42
  Status FinalizeJobImpl(Handle<SharedFunctionInfo> shared_info,
                         Isolate* isolate) final;
43
  Status FinalizeJobImpl(Handle<SharedFunctionInfo> shared_info,
44
                         LocalIsolate* isolate) final;
45 46 47

 private:
  BytecodeGenerator* generator() { return &generator_; }
48 49 50
  template <typename LocalIsolate>
  void CheckAndPrintBytecodeMismatch(LocalIsolate* isolate,
                                     Handle<Script> script,
51
                                     Handle<BytecodeArray> bytecode);
52

53 54 55 56
  template <typename LocalIsolate>
  Status DoFinalizeJobImpl(Handle<SharedFunctionInfo> shared_info,
                           LocalIsolate* isolate);

57
  Zone zone_;
58
  UnoptimizedCompilationInfo compilation_info_;
59 60 61 62 63
  BytecodeGenerator generator_;

  DISALLOW_COPY_AND_ASSIGN(InterpreterCompilationJob);
};

64 65 66
Interpreter::Interpreter(Isolate* isolate)
    : isolate_(isolate),
      interpreter_entry_trampoline_instruction_start_(kNullAddress) {
67
  memset(dispatch_table_, 0, sizeof(dispatch_table_));
68

69 70
  if (FLAG_trace_ignition_dispatches) {
    static const int kBytecodeCount = static_cast<int>(Bytecode::kLast) + 1;
71
    bytecode_dispatch_counters_table_.reset(
72
        new uintptr_t[kBytecodeCount * kBytecodeCount]);
73
    memset(bytecode_dispatch_counters_table_.get(), 0,
74 75
           sizeof(uintptr_t) * kBytecodeCount * kBytecodeCount);
  }
76 77
}

78 79 80 81 82 83 84 85 86 87 88 89 90
namespace {

int BuiltinIndexFromBytecode(Bytecode bytecode, OperandScale operand_scale) {
  int index = BytecodeOperands::OperandScaleAsIndex(operand_scale) *
                  kNumberOfBytecodeHandlers +
              static_cast<int>(bytecode);
  int offset = kBytecodeToBuiltinsMapping[index];
  return offset >= 0 ? Builtins::kFirstBytecodeHandler + offset
                     : Builtins::kIllegalHandler;
}

}  // namespace

91 92
Code Interpreter::GetBytecodeHandler(Bytecode bytecode,
                                     OperandScale operand_scale) {
93 94
  int builtin_index = BuiltinIndexFromBytecode(bytecode, operand_scale);
  Builtins* builtins = isolate_->builtins();
95
  return builtins->builtin(builtin_index);
96 97
}

98
void Interpreter::SetBytecodeHandler(Bytecode bytecode,
99
                                     OperandScale operand_scale, Code handler) {
100
  DCHECK(handler.is_off_heap_trampoline());
101
  DCHECK(handler.kind() == CodeKind::BYTECODE_HANDLER);
102
  size_t index = GetDispatchTableIndex(bytecode, operand_scale);
103
  dispatch_table_[index] = handler.InstructionStart();
104 105
}

106 107 108 109 110
// static
size_t Interpreter::GetDispatchTableIndex(Bytecode bytecode,
                                          OperandScale operand_scale) {
  static const size_t kEntriesPerOperandScale = 1u << kBitsPerByte;
  size_t index = static_cast<size_t>(bytecode);
111 112
  return index + BytecodeOperands::OperandScaleAsIndex(operand_scale) *
                     kEntriesPerOperandScale;
113 114
}

115 116
namespace {

117 118
void MaybePrintAst(ParseInfo* parse_info,
                   UnoptimizedCompilationInfo* compilation_info) {
119
  if (!FLAG_print_ast) return;
120

121
  StdoutStream os;
122 123
  std::unique_ptr<char[]> name = compilation_info->literal()->GetDebugName();
  os << "[generating bytecode for function: " << name.get() << "]" << std::endl;
124 125
#ifdef DEBUG
  os << "--- AST ---" << std::endl
126 127
     << AstPrinter(parse_info->stack_limit())
            .PrintProgram(compilation_info->literal())
128
     << std::endl;
129 130 131
#endif  // DEBUG
}

132 133 134 135 136 137 138 139 140 141 142 143 144 145
bool ShouldPrintBytecode(Handle<SharedFunctionInfo> shared) {
  if (!FLAG_print_bytecode) return false;

  // Checks whether function passed the filter.
  if (shared->is_toplevel()) {
    Vector<const char> filter = CStrVector(FLAG_print_bytecode_filter);
    return (filter.length() == 0) || (filter.length() == 1 && filter[0] == '*');
  } else {
    return shared->PassesFilter(FLAG_print_bytecode_filter);
  }
}

}  // namespace

146 147
InterpreterCompilationJob::InterpreterCompilationJob(
    ParseInfo* parse_info, FunctionLiteral* literal,
148
    AccountingAllocator* allocator,
149
    std::vector<FunctionLiteral*>* eager_inner_literals)
150
    : UnoptimizedCompilationJob(parse_info->stack_limit(), parse_info,
151
                                &compilation_info_),
152 153
      zone_(allocator, ZONE_NAME),
      compilation_info_(&zone_, parse_info, literal),
154
      generator_(&zone_, &compilation_info_, parse_info->ast_string_constants(),
155
                 eager_inner_literals) {}
156 157

InterpreterCompilationJob::Status InterpreterCompilationJob::ExecuteJobImpl() {
158
  RuntimeCallTimerScope runtimeTimerScope(
159
      parse_info()->runtime_call_stats(),
160 161
      RuntimeCallCounterId::kCompileIgnition,
      RuntimeCallStats::kThreadSpecific);
162 163
  // TODO(lpy): add support for background compilation RCS trace.
  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"), "V8.CompileIgnition");
164 165 166 167

  // Print AST if flag is enabled. Note, if compiling on a background thread
  // then ASTs from different functions may be intersperse when printed.
  MaybePrintAst(parse_info(), compilation_info());
168

169
  generator()->GenerateBytecode(stack_limit());
170 171 172 173 174 175 176

  if (generator()->HasStackOverflow()) {
    return FAILED;
  }
  return SUCCEEDED;
}

177
#ifdef DEBUG
178
template <typename LocalIsolate>
179
void InterpreterCompilationJob::CheckAndPrintBytecodeMismatch(
180 181
    LocalIsolate* isolate, Handle<Script> script,
    Handle<BytecodeArray> bytecode) {
182
  int first_mismatch = generator()->CheckBytecodeMatches(*bytecode);
183
  if (first_mismatch >= 0) {
184
    parse_info()->ast_value_factory()->Internalize(isolate);
185 186 187
    DeclarationScope::AllocateScopeInfos(parse_info(), isolate);

    Handle<BytecodeArray> new_bytecode =
188
        generator()->FinalizeBytecode(isolate, script);
189 190 191 192

    std::cerr << "Bytecode mismatch";
#ifdef OBJECT_PRINT
    std::cerr << " found for function: ";
193 194 195
    MaybeHandle<String> maybe_name = parse_info()->literal()->GetName(isolate);
    Handle<String> name;
    if (maybe_name.ToHandle(&name) && name->length() != 0) {
196
      name->PrintUC16(std::cerr);
197 198
    } else {
      std::cerr << "anonymous";
199
    }
200
    Object script_name = script->GetNameOrSourceURL();
201 202
    if (script_name.IsString()) {
      std::cerr << " ";
203
      String::cast(script_name).PrintUC16(std::cerr);
204
      std::cerr << ":" << parse_info()->literal()->start_position();
205 206 207
    }
#endif
    std::cerr << "\nOriginal bytecode:\n";
208 209 210 211 212 213 214 215
    bytecode->Disassemble(std::cerr);
    std::cerr << "\nNew bytecode:\n";
    new_bytecode->Disassemble(std::cerr);
    FATAL("Bytecode mismatch at offset %d\n", first_mismatch);
  }
}
#endif

216
InterpreterCompilationJob::Status InterpreterCompilationJob::FinalizeJobImpl(
217
    Handle<SharedFunctionInfo> shared_info, Isolate* isolate) {
218
  RuntimeCallTimerScope runtimeTimerScope(
219
      parse_info()->runtime_call_stats(),
220
      RuntimeCallCounterId::kCompileIgnitionFinalization);
221 222
  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
               "V8.CompileIgnitionFinalization");
223 224 225 226
  return DoFinalizeJobImpl(shared_info, isolate);
}

InterpreterCompilationJob::Status InterpreterCompilationJob::FinalizeJobImpl(
227
    Handle<SharedFunctionInfo> shared_info, LocalIsolate* isolate) {
228 229 230 231 232 233 234 235 236 237 238
  RuntimeCallTimerScope runtimeTimerScope(
      parse_info()->runtime_call_stats(),
      RuntimeCallCounterId::kCompileBackgroundIgnitionFinalization);
  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
               "V8.CompileIgnitionFinalization");
  return DoFinalizeJobImpl(shared_info, isolate);
}

template <typename LocalIsolate>
InterpreterCompilationJob::Status InterpreterCompilationJob::DoFinalizeJobImpl(
    Handle<SharedFunctionInfo> shared_info, LocalIsolate* isolate) {
239
  Handle<BytecodeArray> bytecodes = compilation_info_.bytecode_array();
240
  if (bytecodes.is_null()) {
241 242
    bytecodes = generator()->FinalizeBytecode(
        isolate, handle(Script::cast(shared_info->script()), isolate));
243 244 245 246 247 248 249 250 251 252 253
    if (generator()->HasStackOverflow()) {
      return FAILED;
    }
    compilation_info()->SetBytecodeArray(bytecodes);
  }

  if (compilation_info()->SourcePositionRecordingMode() ==
      SourcePositionTableBuilder::RecordingMode::RECORD_SOURCE_POSITIONS) {
    Handle<ByteArray> source_position_table =
        generator()->FinalizeSourcePositionTable(isolate);
    bytecodes->set_source_position_table(*source_position_table);
254 255
  }

256
  if (ShouldPrintBytecode(shared_info)) {
257
    StdoutStream os;
258 259
    std::unique_ptr<char[]> name =
        compilation_info()->literal()->GetDebugName();
260 261
    os << "[generated bytecode for function: " << name.get() << " ("
       << shared_info << ")]" << std::endl;
262
    bytecodes->Disassemble(os);
263 264 265
    os << std::flush;
  }

266
#ifdef DEBUG
267 268
  CheckAndPrintBytecodeMismatch(
      isolate, handle(Script::cast(shared_info->script()), isolate), bytecodes);
269 270
#endif

271 272 273
  return SUCCEEDED;
}

274
std::unique_ptr<UnoptimizedCompilationJob> Interpreter::NewCompilationJob(
275 276
    ParseInfo* parse_info, FunctionLiteral* literal,
    AccountingAllocator* allocator,
277
    std::vector<FunctionLiteral*>* eager_inner_literals) {
278
  return std::make_unique<InterpreterCompilationJob>(
279
      parse_info, literal, allocator, eager_inner_literals);
280 281
}

282 283 284 285
std::unique_ptr<UnoptimizedCompilationJob>
Interpreter::NewSourcePositionCollectionJob(
    ParseInfo* parse_info, FunctionLiteral* literal,
    Handle<BytecodeArray> existing_bytecode, AccountingAllocator* allocator) {
286 287
  auto job = std::make_unique<InterpreterCompilationJob>(parse_info, literal,
                                                         allocator, nullptr);
288 289 290 291
  job->compilation_info()->SetBytecodeArray(existing_bytecode);
  return job;
}

292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
void Interpreter::ForEachBytecode(
    const std::function<void(Bytecode, OperandScale)>& f) {
  constexpr OperandScale kOperandScales[] = {
#define VALUE(Name, _) OperandScale::k##Name,
      OPERAND_SCALE_LIST(VALUE)
#undef VALUE
  };

  for (OperandScale operand_scale : kOperandScales) {
    for (int i = 0; i < Bytecodes::kBytecodeCount; i++) {
      f(Bytecodes::FromByte(i), operand_scale);
    }
  }
}

307
void Interpreter::Initialize() {
308
  Builtins* builtins = isolate_->builtins();
309 310 311 312 313

  // Set the interpreter entry trampoline entry point now that builtins are
  // initialized.
  Handle<Code> code = BUILTIN_CODE(isolate_, InterpreterEntryTrampoline);
  DCHECK(builtins->is_initialized());
314 315
  DCHECK(code->is_off_heap_trampoline() ||
         isolate_->heap()->IsImmovable(*code));
316 317 318
  interpreter_entry_trampoline_instruction_start_ = code->InstructionStart();

  // Initialize the dispatch table.
319
  Code illegal = builtins->builtin(Builtins::kIllegalHandler);
320
  int builtin_id = Builtins::kFirstBytecodeHandler;
321 322
  ForEachBytecode([=, &builtin_id](Bytecode bytecode,
                                   OperandScale operand_scale) {
323
    Code handler = illegal;
324 325 326 327 328 329 330 331 332
    if (Bytecodes::BytecodeHasHandler(bytecode, operand_scale)) {
#ifdef DEBUG
      std::string builtin_name(Builtins::name(builtin_id));
      std::string expected_name =
          Bytecodes::ToString(bytecode, operand_scale, "") + "Handler";
      DCHECK_EQ(expected_name, builtin_name);
#endif
      handler = builtins->builtin(builtin_id++);
    }
333 334
    SetBytecodeHandler(bytecode, operand_scale, handler);
  });
335
  DCHECK(builtin_id == Builtins::builtin_count);
336
  DCHECK(IsDispatchTableInitialized());
337 338
}

339
bool Interpreter::IsDispatchTableInitialized() const {
340
  return dispatch_table_[0] != kNullAddress;
341 342
}

343
const char* Interpreter::LookupNameOfBytecodeHandler(const Code code) {
344
  if (code.kind() == CodeKind::BYTECODE_HANDLER) {
345
    return Builtins::name(code.builtin_index());
346 347 348 349
  }
  return nullptr;
}

350 351 352 353 354 355 356 357 358 359 360 361
uintptr_t Interpreter::GetDispatchCounter(Bytecode from, Bytecode to) const {
  int from_index = Bytecodes::ToByte(from);
  int to_index = Bytecodes::ToByte(to);
  return bytecode_dispatch_counters_table_[from_index * kNumberOfBytecodes +
                                           to_index];
}

Local<v8::Object> Interpreter::GetDispatchCountersObject() {
  v8::Isolate* isolate = reinterpret_cast<v8::Isolate*>(isolate_);
  Local<v8::Context> context = isolate->GetCurrentContext();

  Local<v8::Object> counters_map = v8::Object::New(isolate);
362 363 364 365 366 367 368 369 370 371 372 373

  // Output is a JSON-encoded object of objects.
  //
  // The keys on the top level object are source bytecodes,
  // and corresponding value are objects. Keys on these last are the
  // destinations of the dispatch and the value associated is a counter for
  // the correspondent source-destination dispatch chain.
  //
  // Only non-zero counters are written to file, but an entry in the top-level
  // object is always present, even if the value is empty because all counters
  // for that source are zero.

374
  for (int from_index = 0; from_index < kNumberOfBytecodes; ++from_index) {
375
    Bytecode from_bytecode = Bytecodes::FromByte(from_index);
376 377 378 379 380
    Local<v8::Object> counters_row = v8::Object::New(isolate);

    for (int to_index = 0; to_index < kNumberOfBytecodes; ++to_index) {
      Bytecode to_bytecode = Bytecodes::FromByte(to_index);
      uintptr_t counter = GetDispatchCounter(from_bytecode, to_bytecode);
381 382

      if (counter > 0) {
383 384
        std::string to_name = Bytecodes::ToString(to_bytecode);
        Local<v8::String> to_name_object =
385
            v8::String::NewFromUtf8(isolate, to_name.c_str()).ToLocalChecked();
386
        Local<v8::Number> counter_object = v8::Number::New(isolate, counter);
387 388
        CHECK(counters_row
                  ->DefineOwnProperty(context, to_name_object, counter_object)
389
                  .IsJust());
390 391 392
      }
    }

393 394
    std::string from_name = Bytecodes::ToString(from_bytecode);
    Local<v8::String> from_name_object =
395
        v8::String::NewFromUtf8(isolate, from_name.c_str()).ToLocalChecked();
396

397 398 399
    CHECK(
        counters_map->DefineOwnProperty(context, from_name_object, counters_row)
            .IsJust());
400 401
  }

402
  return counters_map;
403 404
}

405 406 407
}  // namespace interpreter
}  // namespace internal
}  // namespace v8