v8/test/cctest/interpreter/bytecode-expectations-printer.cc
Ross McIlroy c820b89bc5 [Interpreter] Remove new.target from fixed frame slot.
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}
2017-08-11 17:04:08 +00:00

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