c820b89bc5
Removes the new.target slot from the interpreter's fixed frame. Instead adds a field to BytecodeArray to get the bytecode's incoming new.target or generator object register. The InterpreterEntryTrampoline then sets this register with the incoming new.target (or generator object) when the function is called. This register can be directly the new.target or generator object variable if they are LOCAL location, otherwise it is a temporary register which is then moved to the variable's location during the function prologue. This fixes a hack in the deoptimizer where we would set the new.target fixed slot to undefined in order to avoid extending it's lifetime through the optimized code - now it's just a standard register and can be optimized away as normal. Bug=v8:6644 Change-Id: Ieb8cc34cccefd9fb6634a90cbc77c6002a54f2ae Reviewed-on: https://chromium-review.googlesource.com/608966 Commit-Queue: Ross McIlroy <rmcilroy@chromium.org> Reviewed-by: Michael Starzinger <mstarzinger@chromium.org> Cr-Commit-Position: refs/heads/master@{#47320}
397 lines
13 KiB
C++
397 lines
13 KiB
C++
// Copyright 2016 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 "test/cctest/interpreter/bytecode-expectations-printer.h"
|
|
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
#include <vector>
|
|
|
|
#include "include/libplatform/libplatform.h"
|
|
#include "include/v8.h"
|
|
|
|
#include "src/api.h"
|
|
#include "src/base/logging.h"
|
|
#include "src/objects-inl.h"
|
|
#include "src/runtime/runtime.h"
|
|
|
|
#include "src/interpreter/bytecode-array-iterator.h"
|
|
#include "src/interpreter/bytecode-generator.h"
|
|
#include "src/interpreter/bytecodes.h"
|
|
#include "src/interpreter/interpreter-intrinsics.h"
|
|
#include "src/interpreter/interpreter.h"
|
|
#include "src/source-position-table.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
namespace interpreter {
|
|
|
|
static const char* NameForNativeContextIntrinsicIndex(uint32_t idx) {
|
|
switch (idx) {
|
|
#define COMPARE_NATIVE_CONTEXT_INTRINSIC_IDX(NAME, Type, name) \
|
|
case Context::NAME: \
|
|
return #name;
|
|
|
|
NATIVE_CONTEXT_INTRINSIC_FUNCTIONS(COMPARE_NATIVE_CONTEXT_INTRINSIC_IDX)
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return "UnknownIntrinsicIndex";
|
|
}
|
|
|
|
// static
|
|
const char* const BytecodeExpectationsPrinter::kDefaultTopFunctionName =
|
|
"__genbckexp_wrapper__";
|
|
const char* const BytecodeExpectationsPrinter::kIndent = " ";
|
|
|
|
v8::Local<v8::String> BytecodeExpectationsPrinter::V8StringFromUTF8(
|
|
const char* data) const {
|
|
return v8::String::NewFromUtf8(isolate_, data, v8::NewStringType::kNormal)
|
|
.ToLocalChecked();
|
|
}
|
|
|
|
std::string BytecodeExpectationsPrinter::WrapCodeInFunction(
|
|
const char* function_name, const std::string& function_body) const {
|
|
std::ostringstream program_stream;
|
|
program_stream << "function " << function_name << "() {" << function_body
|
|
<< "}\n"
|
|
<< function_name << "();";
|
|
|
|
return program_stream.str();
|
|
}
|
|
|
|
v8::Local<v8::Script> BytecodeExpectationsPrinter::CompileScript(
|
|
const char* program) const {
|
|
v8::Local<v8::String> source = V8StringFromUTF8(program);
|
|
return v8::Script::Compile(isolate_->GetCurrentContext(), source)
|
|
.ToLocalChecked();
|
|
}
|
|
|
|
v8::Local<v8::Module> BytecodeExpectationsPrinter::CompileModule(
|
|
const char* program) const {
|
|
ScriptOrigin origin(
|
|
Local<v8::Value>(), Local<v8::Integer>(), Local<v8::Integer>(),
|
|
Local<v8::Boolean>(), Local<v8::Integer>(), Local<v8::Value>(),
|
|
Local<v8::Boolean>(), Local<v8::Boolean>(), True(isolate_));
|
|
v8::ScriptCompiler::Source source(V8StringFromUTF8(program), origin);
|
|
return v8::ScriptCompiler::CompileModule(isolate_, &source).ToLocalChecked();
|
|
}
|
|
|
|
void BytecodeExpectationsPrinter::Run(v8::Local<v8::Script> script) const {
|
|
(void)script->Run(isolate_->GetCurrentContext());
|
|
}
|
|
|
|
i::Handle<v8::internal::BytecodeArray>
|
|
BytecodeExpectationsPrinter::GetBytecodeArrayForGlobal(
|
|
const char* global_name) const {
|
|
const v8::Local<v8::Context>& context = isolate_->GetCurrentContext();
|
|
v8::Local<v8::String> v8_global_name = V8StringFromUTF8(global_name);
|
|
v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
|
|
context->Global()->Get(context, v8_global_name).ToLocalChecked());
|
|
i::Handle<i::JSFunction> js_function =
|
|
i::Handle<i::JSFunction>::cast(v8::Utils::OpenHandle(*function));
|
|
|
|
i::Handle<i::BytecodeArray> bytecodes =
|
|
i::handle(js_function->shared()->bytecode_array(), i_isolate());
|
|
|
|
return bytecodes;
|
|
}
|
|
|
|
i::Handle<i::BytecodeArray>
|
|
BytecodeExpectationsPrinter::GetBytecodeArrayForModule(
|
|
v8::Local<v8::Module> module) const {
|
|
i::Handle<i::Module> i_module = v8::Utils::OpenHandle(*module);
|
|
return i::handle(SharedFunctionInfo::cast(i_module->code())->bytecode_array(),
|
|
i_isolate());
|
|
}
|
|
|
|
i::Handle<i::BytecodeArray>
|
|
BytecodeExpectationsPrinter::GetBytecodeArrayForScript(
|
|
v8::Local<v8::Script> script) const {
|
|
i::Handle<i::JSFunction> js_function = v8::Utils::OpenHandle(*script);
|
|
return i::handle(js_function->shared()->bytecode_array(), i_isolate());
|
|
}
|
|
|
|
void BytecodeExpectationsPrinter::PrintEscapedString(
|
|
std::ostream& stream, const std::string& string) const {
|
|
for (char c : string) {
|
|
switch (c) {
|
|
case '"':
|
|
stream << "\\\"";
|
|
break;
|
|
case '\\':
|
|
stream << "\\\\";
|
|
break;
|
|
default:
|
|
stream << c;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void BytecodeExpectationsPrinter::PrintBytecodeOperand(
|
|
std::ostream& stream, const BytecodeArrayIterator& bytecode_iterator,
|
|
const Bytecode& bytecode, int op_index, int parameter_count) const {
|
|
OperandType op_type = Bytecodes::GetOperandType(bytecode, op_index);
|
|
OperandSize op_size = Bytecodes::GetOperandSize(
|
|
bytecode, op_index, bytecode_iterator.current_operand_scale());
|
|
|
|
const char* size_tag;
|
|
switch (op_size) {
|
|
case OperandSize::kByte:
|
|
size_tag = "8";
|
|
break;
|
|
case OperandSize::kShort:
|
|
size_tag = "16";
|
|
break;
|
|
case OperandSize::kQuad:
|
|
size_tag = "32";
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
return;
|
|
}
|
|
|
|
if (Bytecodes::IsRegisterOperandType(op_type)) {
|
|
Register register_value = bytecode_iterator.GetRegisterOperand(op_index);
|
|
stream << 'R';
|
|
if (op_size != OperandSize::kByte) stream << size_tag;
|
|
if (register_value.is_current_context()) {
|
|
stream << "(context)";
|
|
} else if (register_value.is_function_closure()) {
|
|
stream << "(closure)";
|
|
} else if (register_value.is_parameter()) {
|
|
int parameter_index = register_value.ToParameterIndex(parameter_count);
|
|
if (parameter_index == 0) {
|
|
stream << "(this)";
|
|
} else {
|
|
stream << "(arg" << (parameter_index - 1) << ')';
|
|
}
|
|
} else {
|
|
stream << '(' << register_value.index() << ')';
|
|
}
|
|
} else {
|
|
switch (op_type) {
|
|
case OperandType::kFlag8:
|
|
stream << 'U' << size_tag << '(';
|
|
stream << bytecode_iterator.GetFlagOperand(op_index);
|
|
break;
|
|
case OperandType::kIdx: {
|
|
stream << 'U' << size_tag << '(';
|
|
uint32_t idx = bytecode_iterator.GetIndexOperand(op_index);
|
|
if (bytecode == Bytecode::kCallJSRuntime && op_index == 0) {
|
|
stream << "%" << NameForNativeContextIntrinsicIndex(idx);
|
|
} else {
|
|
stream << idx;
|
|
}
|
|
break;
|
|
}
|
|
case OperandType::kUImm:
|
|
stream << 'U' << size_tag << '(';
|
|
stream << bytecode_iterator.GetUnsignedImmediateOperand(op_index);
|
|
break;
|
|
case OperandType::kImm:
|
|
stream << 'I' << size_tag << '(';
|
|
stream << bytecode_iterator.GetImmediateOperand(op_index);
|
|
break;
|
|
case OperandType::kRegCount:
|
|
stream << 'U' << size_tag << '(';
|
|
stream << bytecode_iterator.GetRegisterCountOperand(op_index);
|
|
break;
|
|
case OperandType::kRuntimeId: {
|
|
stream << 'U' << size_tag << '(';
|
|
Runtime::FunctionId id =
|
|
bytecode_iterator.GetRuntimeIdOperand(op_index);
|
|
stream << "Runtime::k" << i::Runtime::FunctionForId(id)->name;
|
|
break;
|
|
}
|
|
case OperandType::kIntrinsicId: {
|
|
stream << 'U' << size_tag << '(';
|
|
Runtime::FunctionId id =
|
|
bytecode_iterator.GetIntrinsicIdOperand(op_index);
|
|
stream << "Runtime::k" << i::Runtime::FunctionForId(id)->name;
|
|
break;
|
|
}
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
|
|
stream << ')';
|
|
}
|
|
}
|
|
|
|
void BytecodeExpectationsPrinter::PrintBytecode(
|
|
std::ostream& stream, const BytecodeArrayIterator& bytecode_iterator,
|
|
int parameter_count) const {
|
|
Bytecode bytecode = bytecode_iterator.current_bytecode();
|
|
OperandScale operand_scale = bytecode_iterator.current_operand_scale();
|
|
if (Bytecodes::OperandScaleRequiresPrefixBytecode(operand_scale)) {
|
|
Bytecode prefix = Bytecodes::OperandScaleToPrefixBytecode(operand_scale);
|
|
stream << "B(" << Bytecodes::ToString(prefix) << "), ";
|
|
}
|
|
stream << "B(" << Bytecodes::ToString(bytecode) << ')';
|
|
int operands_count = Bytecodes::NumberOfOperands(bytecode);
|
|
for (int op_index = 0; op_index < operands_count; ++op_index) {
|
|
stream << ", ";
|
|
PrintBytecodeOperand(stream, bytecode_iterator, bytecode, op_index,
|
|
parameter_count);
|
|
}
|
|
}
|
|
|
|
void BytecodeExpectationsPrinter::PrintSourcePosition(
|
|
std::ostream& stream, SourcePositionTableIterator& source_iterator,
|
|
int bytecode_offset) const {
|
|
static const size_t kPositionWidth = 4;
|
|
if (!source_iterator.done() &&
|
|
source_iterator.code_offset() == bytecode_offset) {
|
|
stream << "/* " << std::setw(kPositionWidth)
|
|
<< source_iterator.source_position().ScriptOffset();
|
|
if (source_iterator.is_statement()) {
|
|
stream << " S> */ ";
|
|
} else {
|
|
stream << " E> */ ";
|
|
}
|
|
source_iterator.Advance();
|
|
} else {
|
|
stream << " " << std::setw(kPositionWidth) << ' ' << " ";
|
|
}
|
|
}
|
|
|
|
void BytecodeExpectationsPrinter::PrintV8String(std::ostream& stream,
|
|
i::String* string) const {
|
|
stream << '"';
|
|
for (int i = 0, length = string->length(); i < length; ++i) {
|
|
stream << i::AsEscapedUC16ForJSON(string->Get(i));
|
|
}
|
|
stream << '"';
|
|
}
|
|
|
|
void BytecodeExpectationsPrinter::PrintConstant(
|
|
std::ostream& stream, i::Handle<i::Object> constant) const {
|
|
if (constant->IsSmi()) {
|
|
stream << "Smi [";
|
|
i::Smi::cast(*constant)->SmiPrint(stream);
|
|
stream << "]";
|
|
} else {
|
|
stream << i::HeapObject::cast(*constant)->map()->instance_type();
|
|
if (constant->IsHeapNumber()) {
|
|
stream << " [";
|
|
i::HeapNumber::cast(*constant)->HeapNumberPrint(stream);
|
|
stream << "]";
|
|
} else if (constant->IsString()) {
|
|
stream << " [";
|
|
PrintV8String(stream, i::String::cast(*constant));
|
|
stream << "]";
|
|
}
|
|
}
|
|
}
|
|
|
|
void BytecodeExpectationsPrinter::PrintFrameSize(
|
|
std::ostream& stream, i::Handle<i::BytecodeArray> bytecode_array) const {
|
|
const int kPointerSize = sizeof(void*);
|
|
int frame_size = bytecode_array->frame_size();
|
|
|
|
DCHECK_EQ(frame_size % kPointerSize, 0);
|
|
stream << "frame size: " << frame_size / kPointerSize
|
|
<< "\nparameter count: " << bytecode_array->parameter_count() << '\n';
|
|
}
|
|
|
|
void BytecodeExpectationsPrinter::PrintBytecodeSequence(
|
|
std::ostream& stream, i::Handle<i::BytecodeArray> bytecode_array) const {
|
|
stream << "bytecode array length: " << bytecode_array->length()
|
|
<< "\nbytecodes: [\n";
|
|
|
|
SourcePositionTableIterator source_iterator(
|
|
bytecode_array->SourcePositionTable());
|
|
BytecodeArrayIterator bytecode_iterator(bytecode_array);
|
|
for (; !bytecode_iterator.done(); bytecode_iterator.Advance()) {
|
|
stream << kIndent;
|
|
PrintSourcePosition(stream, source_iterator,
|
|
bytecode_iterator.current_offset());
|
|
PrintBytecode(stream, bytecode_iterator, bytecode_array->parameter_count());
|
|
stream << ",\n";
|
|
}
|
|
stream << "]\n";
|
|
}
|
|
|
|
void BytecodeExpectationsPrinter::PrintConstantPool(
|
|
std::ostream& stream, i::FixedArray* constant_pool) const {
|
|
stream << "constant pool: [\n";
|
|
int num_constants = constant_pool->length();
|
|
if (num_constants > 0) {
|
|
for (int i = 0; i < num_constants; ++i) {
|
|
stream << kIndent;
|
|
PrintConstant(stream, i::FixedArray::get(constant_pool, i, i_isolate()));
|
|
stream << ",\n";
|
|
}
|
|
}
|
|
stream << "]\n";
|
|
}
|
|
|
|
void BytecodeExpectationsPrinter::PrintCodeSnippet(
|
|
std::ostream& stream, const std::string& body) const {
|
|
stream << "snippet: \"\n";
|
|
std::stringstream body_stream(body);
|
|
std::string body_line;
|
|
while (std::getline(body_stream, body_line)) {
|
|
stream << kIndent;
|
|
PrintEscapedString(stream, body_line);
|
|
stream << '\n';
|
|
}
|
|
stream << "\"\n";
|
|
}
|
|
|
|
void BytecodeExpectationsPrinter::PrintHandlers(
|
|
std::ostream& stream, i::Handle<i::BytecodeArray> bytecode_array) const {
|
|
stream << "handlers: [\n";
|
|
HandlerTable* table = HandlerTable::cast(bytecode_array->handler_table());
|
|
for (int i = 0, num_entries = table->NumberOfRangeEntries(); i < num_entries;
|
|
++i) {
|
|
stream << " [" << table->GetRangeStart(i) << ", " << table->GetRangeEnd(i)
|
|
<< ", " << table->GetRangeHandler(i) << "],\n";
|
|
}
|
|
stream << "]\n";
|
|
}
|
|
|
|
void BytecodeExpectationsPrinter::PrintBytecodeArray(
|
|
std::ostream& stream, i::Handle<i::BytecodeArray> bytecode_array) const {
|
|
PrintFrameSize(stream, bytecode_array);
|
|
PrintBytecodeSequence(stream, bytecode_array);
|
|
PrintConstantPool(stream, bytecode_array->constant_pool());
|
|
PrintHandlers(stream, bytecode_array);
|
|
}
|
|
|
|
void BytecodeExpectationsPrinter::PrintExpectation(
|
|
std::ostream& stream, const std::string& snippet) const {
|
|
std::string source_code =
|
|
wrap_ ? WrapCodeInFunction(test_function_name_.c_str(), snippet)
|
|
: snippet;
|
|
|
|
i::Handle<i::BytecodeArray> bytecode_array;
|
|
if (module_) {
|
|
CHECK(top_level_ && !wrap_);
|
|
v8::Local<v8::Module> module = CompileModule(source_code.c_str());
|
|
bytecode_array = GetBytecodeArrayForModule(module);
|
|
} else {
|
|
v8::Local<v8::Script> script = CompileScript(source_code.c_str());
|
|
if (top_level_) {
|
|
bytecode_array = GetBytecodeArrayForScript(script);
|
|
} else {
|
|
Run(script);
|
|
bytecode_array = GetBytecodeArrayForGlobal(test_function_name_.c_str());
|
|
}
|
|
}
|
|
|
|
stream << "---\n";
|
|
PrintCodeSnippet(stream, snippet);
|
|
PrintBytecodeArray(stream, bytecode_array);
|
|
stream << '\n';
|
|
}
|
|
|
|
} // namespace interpreter
|
|
} // namespace internal
|
|
} // namespace v8
|