[Interpreter] TurboFan implementation of intrinsics.

Introduces a bytecode whose handler executes the equivalent of %_IsArray and %_IsJSReceiver without a runtime call.

BUG=v8:4822
LOG=y

Review URL: https://codereview.chromium.org/1645763003

Cr-Commit-Position: refs/heads/master@{#34983}
This commit is contained in:
epertoso 2016-03-22 04:35:09 -07:00 committed by Commit bot
parent 27338320f4
commit d158bf14b3
19 changed files with 632 additions and 166 deletions

View File

@ -1133,6 +1133,8 @@ source_set("v8_base") {
"src/interpreter/interpreter.h",
"src/interpreter/interpreter-assembler.cc",
"src/interpreter/interpreter-assembler.h",
"src/interpreter/interpreter-intrinsics.cc",
"src/interpreter/interpreter-intrinsics.h",
"src/interpreter/source-position-table.cc",
"src/interpreter/source-position-table.h",
"src/isolate-inl.h",

View File

@ -231,6 +231,8 @@ namespace internal {
V(kUnexpectedNegativeValue, "Unexpected negative value") \
V(kUnexpectedNumberOfPreAllocatedPropertyFields, \
"Unexpected number of pre-allocated property fields") \
V(kUnexpectedFunctionIDForInvokeIntrinsic, \
"Unexpected runtime function id for the InvokeIntrinsic bytecode") \
V(kUnexpectedFPCRMode, "Unexpected FPCR mode.") \
V(kUnexpectedSmi, "Unexpected smi value") \
V(kUnexpectedStackDepth, "Unexpected operand stack depth in full-codegen") \
@ -263,6 +265,8 @@ namespace internal {
V(kWrongFunctionContext, "Wrong context passed to function") \
V(kWrongAddressOrValuePassedToRecordWrite, \
"Wrong address or value passed to RecordWrite") \
V(kWrongArgumentCountForInvokeIntrinsic, \
"Wrong number of arguments for intrinsic") \
V(kShouldNotDirectlyEnterOsrFunction, \
"Should not directly enter OSR-compiled function") \
V(kYield, "Yield")

View File

@ -983,6 +983,20 @@ void BytecodeGraphBuilder::VisitCallRuntimeForPair() {
environment()->BindRegistersToProjections(first_return, return_pair, &states);
}
void BytecodeGraphBuilder::VisitInvokeIntrinsic() {
FrameStateBeforeAndAfter states(this);
Runtime::FunctionId functionId = static_cast<Runtime::FunctionId>(
bytecode_iterator().GetRuntimeIdOperand(0));
interpreter::Register first_arg = bytecode_iterator().GetRegisterOperand(1);
size_t arg_count = bytecode_iterator().GetRegisterCountOperand(2);
// Create node to perform the runtime call. Turbofan will take care of the
// lowering.
const Operator* call = javascript()->CallRuntime(functionId, arg_count);
Node* value = ProcessCallRuntimeArguments(call, first_arg, arg_count);
environment()->BindAccumulator(value, &states);
}
Node* BytecodeGraphBuilder::ProcessCallNewArguments(
const Operator* call_new_op, Node* callee, Node* new_target,
interpreter::Register first_arg, size_t arity) {

View File

@ -128,6 +128,7 @@ class BytecodeGraphBuilder {
void BuildCastOperator(const Operator* op);
void BuildForInPrepare();
void BuildForInNext();
void BuildInvokeIntrinsic();
// Control flow plumbing.
void BuildJump();

View File

@ -4,6 +4,7 @@
#include "src/interpreter/bytecode-array-builder.h"
#include "src/compiler.h"
#include "src/interpreter/interpreter-intrinsics.h"
namespace v8 {
namespace internal {
@ -1026,11 +1027,13 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntime(
DCHECK_EQ(0u, arg_count);
first_arg = Register(0);
}
Bytecode bytecode = IntrinsicsHelper::IsSupported(function_id)
? Bytecode::kInvokeIntrinsic
: Bytecode::kCallRuntime;
OperandScale operand_scale = OperandSizesToScale(
SizeForRegisterOperand(first_arg), SizeForUnsignedOperand(arg_count));
OutputScaled(Bytecode::kCallRuntime, operand_scale,
static_cast<uint16_t>(function_id), RegisterOperand(first_arg),
UnsignedOperand(arg_count));
OutputScaled(bytecode, operand_scale, static_cast<uint16_t>(function_id),
RegisterOperand(first_arg), UnsignedOperand(arg_count));
return *this;
}

View File

@ -308,7 +308,8 @@ bool Bytecodes::IsCallOrNew(Bytecode bytecode) {
// static
bool Bytecodes::IsCallRuntime(Bytecode bytecode) {
return bytecode == Bytecode::kCallRuntime ||
bytecode == Bytecode::kCallRuntimeForPair;
bytecode == Bytecode::kCallRuntimeForPair ||
bytecode == Bytecode::kInvokeIntrinsic;
}
// static

View File

@ -156,6 +156,10 @@ namespace interpreter {
V(CallJSRuntime, OperandType::kIdx, OperandType::kReg, \
OperandType::kRegCount) \
\
/* Intrinsics */ \
V(InvokeIntrinsic, OperandType::kRuntimeId, OperandType::kMaybeReg, \
OperandType::kRegCount) \
\
/* New operator */ \
V(New, OperandType::kReg, OperandType::kMaybeReg, OperandType::kRegCount) \
\

View File

@ -0,0 +1,159 @@
// 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-intrinsics.h"
namespace v8 {
namespace internal {
namespace interpreter {
using compiler::Node;
#define __ assembler_->
IntrinsicsHelper::IntrinsicsHelper(InterpreterAssembler* assembler)
: assembler_(assembler) {}
bool IntrinsicsHelper::IsSupported(Runtime::FunctionId function_id) {
switch (function_id) {
#define SUPPORTED(name, lower_case, count) case Runtime::kInline##name:
INTRINSICS_LIST(SUPPORTED)
return true;
#undef SUPPORTED
default:
return false;
}
}
Node* IntrinsicsHelper::InvokeIntrinsic(Node* function_id, Node* context,
Node* first_arg_reg, Node* arg_count) {
InterpreterAssembler::Label abort(assembler_), end(assembler_);
InterpreterAssembler::Variable result(assembler_,
MachineRepresentation::kTagged);
#define MAKE_LABEL(name, lower_case, count) \
InterpreterAssembler::Label lower_case(assembler_);
INTRINSICS_LIST(MAKE_LABEL)
#undef MAKE_LABEL
#define LABEL_POINTER(name, lower_case, count) &lower_case,
InterpreterAssembler::Label* labels[] = {INTRINSICS_LIST(LABEL_POINTER)};
#undef LABEL_POINTER
#define CASE(name, lower_case, count) \
static_cast<int32_t>(Runtime::kInline##name),
int32_t cases[] = {INTRINSICS_LIST(CASE)};
#undef CASE
__ Switch(function_id, &abort, cases, labels, arraysize(cases));
#define HANDLE_CASE(name, lower_case, expected_arg_count) \
__ Bind(&lower_case); \
if (FLAG_debug_code) { \
AbortIfArgCountMismatch(expected_arg_count, arg_count); \
} \
result.Bind(name(first_arg_reg)); \
__ Goto(&end);
INTRINSICS_LIST(HANDLE_CASE)
#undef HANDLE_CASE
__ Bind(&abort);
__ Abort(BailoutReason::kUnexpectedFunctionIDForInvokeIntrinsic);
result.Bind(__ UndefinedConstant());
__ Goto(&end);
__ Bind(&end);
return result.value();
}
Node* IntrinsicsHelper::CompareInstanceType(Node* map, int type,
InstanceTypeCompareMode mode) {
InterpreterAssembler::Variable return_value(assembler_,
MachineRepresentation::kTagged);
Node* instance_type = __ LoadInstanceType(map);
InterpreterAssembler::Label if_true(assembler_), if_false(assembler_),
end(assembler_);
Node* condition;
if (mode == kInstanceTypeEqual) {
condition = __ Word32Equal(instance_type, __ Int32Constant(type));
} else {
DCHECK(mode == kInstanceTypeGreaterThanOrEqual);
condition =
__ Int32GreaterThanOrEqual(instance_type, __ Int32Constant(type));
}
__ Branch(condition, &if_true, &if_false);
__ Bind(&if_true);
return_value.Bind(__ BooleanConstant(true));
__ Goto(&end);
__ Bind(&if_false);
return_value.Bind(__ BooleanConstant(false));
__ Goto(&end);
__ Bind(&end);
return return_value.value();
}
Node* IntrinsicsHelper::IsJSReceiver(Node* input) {
InterpreterAssembler::Variable return_value(assembler_,
MachineRepresentation::kTagged);
InterpreterAssembler::Label if_smi(assembler_), if_not_smi(assembler_),
end(assembler_);
Node* arg = __ LoadRegister(input);
__ Branch(__ WordIsSmi(arg), &if_smi, &if_not_smi);
__ Bind(&if_smi);
return_value.Bind(__ BooleanConstant(false));
__ Goto(&end);
__ Bind(&if_not_smi);
STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE);
return_value.Bind(CompareInstanceType(arg, FIRST_JS_RECEIVER_TYPE,
kInstanceTypeGreaterThanOrEqual));
__ Goto(&end);
__ Bind(&end);
return return_value.value();
}
Node* IntrinsicsHelper::IsArray(Node* input) {
InterpreterAssembler::Variable return_value(assembler_,
MachineRepresentation::kTagged);
InterpreterAssembler::Label if_smi(assembler_), if_not_smi(assembler_),
end(assembler_);
Node* arg = __ LoadRegister(input);
__ Branch(__ WordIsSmi(arg), &if_smi, &if_not_smi);
__ Bind(&if_smi);
return_value.Bind(__ BooleanConstant(false));
__ Goto(&end);
__ Bind(&if_not_smi);
return_value.Bind(
CompareInstanceType(arg, JS_ARRAY_TYPE, kInstanceTypeEqual));
__ Goto(&end);
__ Bind(&end);
return return_value.value();
}
void IntrinsicsHelper::AbortIfArgCountMismatch(int expected, Node* actual) {
InterpreterAssembler::Label match(assembler_), mismatch(assembler_),
end(assembler_);
Node* comparison = __ Word32Equal(actual, __ Int32Constant(expected));
__ Branch(comparison, &match, &mismatch);
__ Bind(&mismatch);
__ Abort(kWrongArgumentCountForInvokeIntrinsic);
__ Goto(&end);
__ Bind(&match);
__ Goto(&end);
__ Bind(&end);
}
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,62 @@
// 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.
#ifndef V8_INTERPRETER_INTERPRETER_INTRINSICS_H_
#define V8_INTERPRETER_INTERPRETER_INTRINSICS_H_
#include "src/allocation.h"
#include "src/base/smart-pointers.h"
#include "src/builtins.h"
#include "src/frames.h"
#include "src/interpreter/bytecodes.h"
#include "src/interpreter/interpreter-assembler.h"
#include "src/runtime/runtime.h"
namespace v8 {
namespace internal {
namespace compiler {
class Node;
} // namespace compiler
#define INTRINSICS_LIST(V) \
V(IsJSReceiver, is_js_receiver, 1) \
V(IsArray, is_array, 1)
namespace interpreter {
class IntrinsicsHelper {
public:
explicit IntrinsicsHelper(InterpreterAssembler* assembler);
compiler::Node* InvokeIntrinsic(compiler::Node* function_id,
compiler::Node* context,
compiler::Node* first_arg_reg,
compiler::Node* arg_count);
static bool IsSupported(Runtime::FunctionId function_id);
private:
enum InstanceTypeCompareMode {
kInstanceTypeEqual,
kInstanceTypeGreaterThanOrEqual
};
compiler::Node* CompareInstanceType(compiler::Node* map, int type,
InstanceTypeCompareMode mode);
void AbortIfArgCountMismatch(int expected, compiler::Node* actual);
InterpreterAssembler* assembler_;
#define DECLARE_INTRINSIC_HELPER(name, lower_case, count) \
compiler::Node* name(compiler::Node* input);
INTRINSICS_LIST(DECLARE_INTRINSIC_HELPER)
#undef DECLARE_INTRINSIC_HELPER
DISALLOW_COPY_AND_ASSIGN(IntrinsicsHelper);
};
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif

View File

@ -11,6 +11,7 @@
#include "src/interpreter/bytecode-generator.h"
#include "src/interpreter/bytecodes.h"
#include "src/interpreter/interpreter-assembler.h"
#include "src/interpreter/interpreter-intrinsics.h"
#include "src/log.h"
#include "src/zone.h"
@ -894,6 +895,23 @@ void Interpreter::DoCallRuntime(InterpreterAssembler* assembler) {
DoCallRuntimeCommon(assembler);
}
// InvokeIntrinsic <function_id> <first_arg> <arg_count>
//
// Implements the semantic equivalent of calling the runtime function
// |function_id| with the first argument in |first_arg| and |arg_count|
// arguments in subsequent registers.
void Interpreter::DoInvokeIntrinsic(InterpreterAssembler* assembler) {
Node* function_id = __ BytecodeOperandRuntimeId(0);
Node* first_arg_reg = __ BytecodeOperandReg(1);
Node* arg_count = __ BytecodeOperandCount(2);
Node* context = __ GetContext();
IntrinsicsHelper helper(assembler);
Node* result =
helper.InvokeIntrinsic(function_id, context, first_arg_reg, arg_count);
__ SetAccumulator(result);
__ Dispatch();
}
void Interpreter::DoCallRuntimeForPairCommon(InterpreterAssembler* assembler) {
// Call the runtime function.
Node* function_id = __ BytecodeOperandRuntimeId(0);

View File

@ -89,8 +89,10 @@
'cctest.cc',
'expression-type-collector.cc',
'expression-type-collector.h',
'interpreter/interpreter-tester.cc',
'interpreter/test-bytecode-generator.cc',
'interpreter/test-interpreter.cc',
'interpreter/test-interpreter-intrinsics.cc',
'interpreter/bytecode-expectations-printer.cc',
'interpreter/bytecode-expectations-printer.h',
'gay-fixed.cc',

View File

@ -659,6 +659,28 @@ TEST(BytecodeGraphBuilderCallRuntime) {
}
}
TEST(BytecodeGraphBuilderInvokeIntrinsic) {
HandleAndZoneScope scope;
Isolate* isolate = scope.main_isolate();
Zone* zone = scope.main_zone();
Factory* factory = isolate->factory();
ExpectedSnippet<1> snippets[] = {
{"function f(arg0) { return %_IsJSReceiver(arg0); }\nf()",
{factory->false_value(), factory->NewNumberFromInt(1)}},
{"function f(arg0) { return %_IsArray(arg0) }\nf(undefined)",
{factory->true_value(), BytecodeGraphTester::NewObject("[1, 2, 3]")}},
};
for (size_t i = 0; i < arraysize(snippets); i++) {
BytecodeGraphTester tester(isolate, zone, snippets[i].code_snippet);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> return_value =
callable(snippets[i].parameter(0)).ToHandleChecked();
CHECK(return_value->SameValue(*snippets[i].return_value()));
}
}
void TestBytecodeGraphBuilderGlobals(size_t shard) {
HandleAndZoneScope scope;
Isolate* isolate = scope.main_isolate();

View File

@ -36,7 +36,7 @@ bytecodes: [
B(Call), R(14), R(15), U8(1), U8(5),
B(Star), R(2),
B(Star), R(13),
B(CallRuntime), U16(Runtime::k_IsJSReceiver), R(13), U8(1),
B(InvokeIntrinsic), U16(Runtime::k_IsJSReceiver), R(13), U8(1),
B(LogicalNot),
B(JumpIfFalse), U8(11),
B(Ldar), R(2),
@ -151,7 +151,7 @@ bytecodes: [
B(CallRuntime), U16(Runtime::k_Call), R(12), U8(2),
B(Star), R(6),
B(Star), R(12),
B(CallRuntime), U16(Runtime::k_IsJSReceiver), R(12), U8(1),
B(InvokeIntrinsic), U16(Runtime::k_IsJSReceiver), R(12), U8(1),
B(JumpIfToBooleanFalse), U8(4),
B(Jump), U8(11),
B(Ldar), R(6),
@ -217,7 +217,7 @@ bytecodes: [
B(Call), R(15), R(16), U8(1), U8(5),
B(Star), R(2),
B(Star), R(14),
B(CallRuntime), U16(Runtime::k_IsJSReceiver), R(14), U8(1),
B(InvokeIntrinsic), U16(Runtime::k_IsJSReceiver), R(14), U8(1),
B(LogicalNot),
B(JumpIfFalse), U8(11),
B(Ldar), R(2),
@ -334,7 +334,7 @@ bytecodes: [
B(CallRuntime), U16(Runtime::k_Call), R(13), U8(2),
B(Star), R(6),
B(Star), R(13),
B(CallRuntime), U16(Runtime::k_IsJSReceiver), R(13), U8(1),
B(InvokeIntrinsic), U16(Runtime::k_IsJSReceiver), R(13), U8(1),
B(JumpIfToBooleanFalse), U8(4),
B(Jump), U8(11),
B(Ldar), R(6),
@ -405,7 +405,7 @@ bytecodes: [
B(Call), R(14), R(15), U8(1), U8(5),
B(Star), R(2),
B(Star), R(13),
B(CallRuntime), U16(Runtime::k_IsJSReceiver), R(13), U8(1),
B(InvokeIntrinsic), U16(Runtime::k_IsJSReceiver), R(13), U8(1),
B(LogicalNot),
B(JumpIfFalse), U8(11),
B(Ldar), R(2),
@ -531,7 +531,7 @@ bytecodes: [
B(CallRuntime), U16(Runtime::k_Call), R(12), U8(2),
B(Star), R(6),
B(Star), R(12),
B(CallRuntime), U16(Runtime::k_IsJSReceiver), R(12), U8(1),
B(InvokeIntrinsic), U16(Runtime::k_IsJSReceiver), R(12), U8(1),
B(JumpIfToBooleanFalse), U8(4),
B(Jump), U8(11),
B(Ldar), R(6),
@ -598,7 +598,7 @@ bytecodes: [
B(Call), R(13), R(14), U8(1), U8(5),
B(Star), R(1),
B(Star), R(12),
B(CallRuntime), U16(Runtime::k_IsJSReceiver), R(12), U8(1),
B(InvokeIntrinsic), U16(Runtime::k_IsJSReceiver), R(12), U8(1),
B(LogicalNot),
B(JumpIfFalse), U8(11),
B(Ldar), R(1),
@ -718,7 +718,7 @@ bytecodes: [
B(CallRuntime), U16(Runtime::k_Call), R(11), U8(2),
B(Star), R(5),
B(Star), R(11),
B(CallRuntime), U16(Runtime::k_IsJSReceiver), R(11), U8(1),
B(InvokeIntrinsic), U16(Runtime::k_IsJSReceiver), R(11), U8(1),
B(JumpIfToBooleanFalse), U8(4),
B(Jump), U8(11),
B(Ldar), R(5),

View File

@ -0,0 +1,79 @@
// 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 "test/cctest/interpreter/interpreter-tester.h"
namespace v8 {
namespace internal {
namespace interpreter {
MaybeHandle<Object> CallInterpreter(Isolate* isolate,
Handle<JSFunction> function) {
return Execution::Call(isolate, function,
isolate->factory()->undefined_value(), 0, nullptr);
}
InterpreterTester::InterpreterTester(
Isolate* isolate, const char* source, MaybeHandle<BytecodeArray> bytecode,
MaybeHandle<TypeFeedbackVector> feedback_vector, const char* filter)
: isolate_(isolate),
source_(source),
bytecode_(bytecode),
feedback_vector_(feedback_vector) {
i::FLAG_ignition = true;
i::FLAG_always_opt = false;
// Set ignition filter flag via SetFlagsFromString to avoid double-free
// (or potential leak with StrDup() based on ownership confusion).
ScopedVector<char> ignition_filter(64);
SNPrintF(ignition_filter, "--ignition-filter=%s", filter);
FlagList::SetFlagsFromString(ignition_filter.start(),
ignition_filter.length());
// Ensure handler table is generated.
isolate->interpreter()->Initialize();
}
InterpreterTester::InterpreterTester(
Isolate* isolate, Handle<BytecodeArray> bytecode,
MaybeHandle<TypeFeedbackVector> feedback_vector, const char* filter)
: InterpreterTester(isolate, nullptr, bytecode, feedback_vector, filter) {}
InterpreterTester::InterpreterTester(Isolate* isolate, const char* source,
const char* filter)
: InterpreterTester(isolate, source, MaybeHandle<BytecodeArray>(),
MaybeHandle<TypeFeedbackVector>(), filter) {}
InterpreterTester::~InterpreterTester() {}
Local<Message> InterpreterTester::CheckThrowsReturnMessage() {
TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate_));
auto callable = GetCallable<>();
MaybeHandle<Object> no_result = callable();
CHECK(isolate_->has_pending_exception());
CHECK(try_catch.HasCaught());
CHECK(no_result.is_null());
isolate_->OptionalRescheduleException(true);
CHECK(!try_catch.Message().IsEmpty());
return try_catch.Message();
}
Handle<Object> InterpreterTester::NewObject(const char* script) {
return v8::Utils::OpenHandle(*CompileRun(script));
}
Handle<String> InterpreterTester::GetName(Isolate* isolate, const char* name) {
Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(name);
return isolate->factory()->string_table()->LookupString(isolate, result);
}
std::string InterpreterTester::SourceForBody(const char* body) {
return "function " + function_name() + "() {\n" + std::string(body) + "\n}";
}
std::string InterpreterTester::function_name() {
return std::string(kFunctionName);
}
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,128 @@
// 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/v8.h"
#include "src/execution.h"
#include "src/handles.h"
#include "src/interpreter/bytecode-array-builder.h"
#include "src/interpreter/interpreter.h"
#include "test/cctest/cctest.h"
#include "test/cctest/test-feedback-vector.h"
namespace v8 {
namespace internal {
namespace interpreter {
MaybeHandle<Object> CallInterpreter(Isolate* isolate,
Handle<JSFunction> function);
template <class... A>
static MaybeHandle<Object> CallInterpreter(Isolate* isolate,
Handle<JSFunction> function,
A... args) {
Handle<Object> argv[] = {args...};
return Execution::Call(isolate, function,
isolate->factory()->undefined_value(), sizeof...(args),
argv);
}
template <class... A>
class InterpreterCallable {
public:
InterpreterCallable(Isolate* isolate, Handle<JSFunction> function)
: isolate_(isolate), function_(function) {}
virtual ~InterpreterCallable() {}
MaybeHandle<Object> operator()(A... args) {
return CallInterpreter(isolate_, function_, args...);
}
private:
Isolate* isolate_;
Handle<JSFunction> function_;
};
namespace {
const char kFunctionName[] = "f";
} // namespace
class InterpreterTester {
public:
InterpreterTester(Isolate* isolate, const char* source,
MaybeHandle<BytecodeArray> bytecode,
MaybeHandle<TypeFeedbackVector> feedback_vector,
const char* filter);
InterpreterTester(Isolate* isolate, Handle<BytecodeArray> bytecode,
MaybeHandle<TypeFeedbackVector> feedback_vector =
MaybeHandle<TypeFeedbackVector>(),
const char* filter = kFunctionName);
InterpreterTester(Isolate* isolate, const char* source,
const char* filter = kFunctionName);
virtual ~InterpreterTester();
template <class... A>
InterpreterCallable<A...> GetCallable() {
return InterpreterCallable<A...>(isolate_, GetBytecodeFunction<A...>());
}
Local<Message> CheckThrowsReturnMessage();
static Handle<Object> NewObject(const char* script);
static Handle<String> GetName(Isolate* isolate, const char* name);
static std::string SourceForBody(const char* body);
static std::string function_name();
private:
Isolate* isolate_;
const char* source_;
MaybeHandle<BytecodeArray> bytecode_;
MaybeHandle<TypeFeedbackVector> feedback_vector_;
template <class... A>
Handle<JSFunction> GetBytecodeFunction() {
Handle<JSFunction> function;
if (source_) {
CompileRun(source_);
v8::Local<v8::Context> context =
v8::Isolate::GetCurrent()->GetCurrentContext();
Local<Function> api_function =
Local<Function>::Cast(CcTest::global()
->Get(context, v8_str(kFunctionName))
.ToLocalChecked());
function = Handle<JSFunction>::cast(v8::Utils::OpenHandle(*api_function));
} else {
int arg_count = sizeof...(A);
std::string source("(function " + function_name() + "(");
for (int i = 0; i < arg_count; i++) {
source += i == 0 ? "a" : ", a";
}
source += "){})";
function = Handle<JSFunction>::cast(v8::Utils::OpenHandle(
*v8::Local<v8::Function>::Cast(CompileRun(source.c_str()))));
function->ReplaceCode(
*isolate_->builtins()->InterpreterEntryTrampoline());
}
if (!bytecode_.is_null()) {
function->shared()->set_function_data(*bytecode_.ToHandleChecked());
}
if (!feedback_vector_.is_null()) {
function->shared()->set_feedback_vector(
*feedback_vector_.ToHandleChecked());
}
return function;
}
DISALLOW_COPY_AND_ASSIGN(InterpreterTester);
};
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,96 @@
// 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/v8.h"
#include "src/interpreter/interpreter-intrinsics.h"
#include "test/cctest/interpreter/interpreter-tester.h"
namespace v8 {
namespace internal {
namespace interpreter {
namespace {
class InvokeIntrinsicHelper {
public:
InvokeIntrinsicHelper(Isolate* isolate, Zone* zone,
Runtime::FunctionId function_id)
: isolate_(isolate),
zone_(zone),
factory_(isolate->factory()),
function_id_(function_id) {}
template <class... A>
Handle<Object> Invoke(A... args) {
CHECK(IntrinsicsHelper::IsSupported(function_id_));
BytecodeArrayBuilder builder(isolate_, zone_, sizeof...(args), 0, 0);
builder.CallRuntime(function_id_, builder.Parameter(0), sizeof...(args))
.Return();
InterpreterTester tester(isolate_, builder.ToBytecodeArray());
auto callable = tester.GetCallable<Handle<Object>>();
return callable(args...).ToHandleChecked();
}
Handle<Object> NewObject(const char* script) {
return v8::Utils::OpenHandle(*CompileRun(script));
}
Handle<Object> Undefined() { return factory_->undefined_value(); }
Handle<Object> Null() { return factory_->null_value(); }
private:
Isolate* isolate_;
Zone* zone_;
Factory* factory_;
Runtime::FunctionId function_id_;
};
} // namespace
TEST(IsJSReceiver) {
HandleAndZoneScope handles;
InvokeIntrinsicHelper helper(handles.main_isolate(), handles.main_zone(),
Runtime::kInlineIsJSReceiver);
Factory* factory = handles.main_isolate()->factory();
CHECK_EQ(*factory->true_value(),
*helper.Invoke(helper.NewObject("new Date()")));
CHECK_EQ(*factory->true_value(),
*helper.Invoke(helper.NewObject("(function() {})")));
CHECK_EQ(*factory->true_value(), *helper.Invoke(helper.NewObject("([1])")));
CHECK_EQ(*factory->true_value(), *helper.Invoke(helper.NewObject("({})")));
CHECK_EQ(*factory->true_value(), *helper.Invoke(helper.NewObject("(/x/)")));
CHECK_EQ(*factory->false_value(), *helper.Invoke(helper.Undefined()));
CHECK_EQ(*factory->false_value(), *helper.Invoke(helper.Null()));
CHECK_EQ(*factory->false_value(),
*helper.Invoke(helper.NewObject("'string'")));
CHECK_EQ(*factory->false_value(), *helper.Invoke(helper.NewObject("42")));
}
TEST(IsArray) {
HandleAndZoneScope handles;
InvokeIntrinsicHelper helper(handles.main_isolate(), handles.main_zone(),
Runtime::kInlineIsArray);
Factory* factory = handles.main_isolate()->factory();
CHECK_EQ(*factory->false_value(),
*helper.Invoke(helper.NewObject("new Date()")));
CHECK_EQ(*factory->false_value(),
*helper.Invoke(helper.NewObject("(function() {})")));
CHECK_EQ(*factory->true_value(), *helper.Invoke(helper.NewObject("([1])")));
CHECK_EQ(*factory->false_value(), *helper.Invoke(helper.NewObject("({})")));
CHECK_EQ(*factory->false_value(), *helper.Invoke(helper.NewObject("(/x/)")));
CHECK_EQ(*factory->false_value(), *helper.Invoke(helper.Undefined()));
CHECK_EQ(*factory->false_value(), *helper.Invoke(helper.Null()));
CHECK_EQ(*factory->false_value(),
*helper.Invoke(helper.NewObject("'string'")));
CHECK_EQ(*factory->false_value(), *helper.Invoke(helper.NewObject("42")));
}
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -9,6 +9,7 @@
#include "src/interpreter/bytecode-array-builder.h"
#include "src/interpreter/interpreter.h"
#include "test/cctest/cctest.h"
#include "test/cctest/interpreter/interpreter-tester.h"
#include "test/cctest/test-feedback-vector.h"
namespace v8 {
@ -16,160 +17,6 @@ namespace internal {
namespace interpreter {
static MaybeHandle<Object> CallInterpreter(Isolate* isolate,
Handle<JSFunction> function) {
return Execution::Call(isolate, function,
isolate->factory()->undefined_value(), 0, nullptr);
}
template <class... A>
static MaybeHandle<Object> CallInterpreter(Isolate* isolate,
Handle<JSFunction> function,
A... args) {
Handle<Object> argv[] = { args... };
return Execution::Call(isolate, function,
isolate->factory()->undefined_value(), sizeof...(args),
argv);
}
template <class... A>
class InterpreterCallable {
public:
InterpreterCallable(Isolate* isolate, Handle<JSFunction> function)
: isolate_(isolate), function_(function) {}
virtual ~InterpreterCallable() {}
MaybeHandle<Object> operator()(A... args) {
return CallInterpreter(isolate_, function_, args...);
}
private:
Isolate* isolate_;
Handle<JSFunction> function_;
};
static const char* kFunctionName = "f";
class InterpreterTester {
public:
InterpreterTester(Isolate* isolate, const char* source,
MaybeHandle<BytecodeArray> bytecode,
MaybeHandle<TypeFeedbackVector> feedback_vector,
const char* filter)
: isolate_(isolate),
source_(source),
bytecode_(bytecode),
feedback_vector_(feedback_vector) {
i::FLAG_ignition = true;
i::FLAG_always_opt = false;
// Set ignition filter flag via SetFlagsFromString to avoid double-free
// (or potential leak with StrDup() based on ownership confusion).
ScopedVector<char> ignition_filter(64);
SNPrintF(ignition_filter, "--ignition-filter=%s", filter);
FlagList::SetFlagsFromString(ignition_filter.start(),
ignition_filter.length());
// Ensure handler table is generated.
isolate->interpreter()->Initialize();
}
InterpreterTester(Isolate* isolate, Handle<BytecodeArray> bytecode,
MaybeHandle<TypeFeedbackVector> feedback_vector =
MaybeHandle<TypeFeedbackVector>(),
const char* filter = kFunctionName)
: InterpreterTester(isolate, nullptr, bytecode, feedback_vector, filter) {
}
InterpreterTester(Isolate* isolate, const char* source,
const char* filter = kFunctionName)
: InterpreterTester(isolate, source, MaybeHandle<BytecodeArray>(),
MaybeHandle<TypeFeedbackVector>(), filter) {}
virtual ~InterpreterTester() {}
template <class... A>
InterpreterCallable<A...> GetCallable() {
return InterpreterCallable<A...>(isolate_, GetBytecodeFunction<A...>());
}
Local<Message> CheckThrowsReturnMessage() {
TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate_));
auto callable = GetCallable<>();
MaybeHandle<Object> no_result = callable();
CHECK(isolate_->has_pending_exception());
CHECK(try_catch.HasCaught());
CHECK(no_result.is_null());
isolate_->OptionalRescheduleException(true);
CHECK(!try_catch.Message().IsEmpty());
return try_catch.Message();
}
static Handle<Object> NewObject(const char* script) {
return v8::Utils::OpenHandle(*CompileRun(script));
}
static Handle<String> GetName(Isolate* isolate, const char* name) {
Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(name);
return isolate->factory()->string_table()->LookupString(isolate, result);
}
static std::string SourceForBody(const char* body) {
return "function " + function_name() + "() {\n" + std::string(body) + "\n}";
}
static std::string function_name() {
return std::string(kFunctionName);
}
private:
Isolate* isolate_;
const char* source_;
MaybeHandle<BytecodeArray> bytecode_;
MaybeHandle<TypeFeedbackVector> feedback_vector_;
template <class... A>
Handle<JSFunction> GetBytecodeFunction() {
Handle<JSFunction> function;
if (source_) {
CompileRun(source_);
v8::Local<v8::Context> context =
v8::Isolate::GetCurrent()->GetCurrentContext();
Local<Function> api_function =
Local<Function>::Cast(CcTest::global()
->Get(context, v8_str(kFunctionName))
.ToLocalChecked());
function = Handle<JSFunction>::cast(v8::Utils::OpenHandle(*api_function));
} else {
int arg_count = sizeof...(A);
std::string source("(function " + function_name() + "(");
for (int i = 0; i < arg_count; i++) {
source += i == 0 ? "a" : ", a";
}
source += "){})";
function = Handle<JSFunction>::cast(v8::Utils::OpenHandle(
*v8::Local<v8::Function>::Cast(CompileRun(source.c_str()))));
function->ReplaceCode(
*isolate_->builtins()->InterpreterEntryTrampoline());
}
if (!bytecode_.is_null()) {
function->shared()->set_function_data(*bytecode_.ToHandleChecked());
}
if (!feedback_vector_.is_null()) {
function->shared()->set_feedback_vector(
*feedback_vector_.ToHandleChecked());
}
return function;
}
DISALLOW_COPY_AND_ASSIGN(InterpreterTester);
};
TEST(InterpreterReturn) {
HandleAndZoneScope handles;
Handle<Object> undefined_value =
@ -1639,6 +1486,24 @@ TEST(InterpreterCallRuntime) {
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(55));
}
TEST(InterpreterInvokeIntrinsic) {
HandleAndZoneScope handles;
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone(), 1,
0, 2);
builder.LoadLiteral(Smi::FromInt(15))
.StoreAccumulatorInRegister(Register(0))
.CallRuntime(Runtime::kInlineIsArray, Register(0), 1)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK(return_val->IsBoolean());
CHECK_EQ(return_val->BooleanValue(), false);
}
TEST(InterpreterFunctionLiteral) {
HandleAndZoneScope handles;

View File

@ -289,6 +289,10 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
.BinaryOperation(Token::Value::ADD, reg)
.JumpIfFalse(&start);
// Intrinsics handled by the interpreter.
builder.CallRuntime(Runtime::kInlineIsArray, reg, 1)
.CallRuntime(Runtime::kInlineIsArray, wide, 1);
builder.Debugger();
for (size_t i = 0; i < arraysize(end); i++) {
builder.Bind(&end[i]);

View File

@ -960,6 +960,8 @@
'../../src/interpreter/interpreter.h',
'../../src/interpreter/interpreter-assembler.cc',
'../../src/interpreter/interpreter-assembler.h',
'../../src/interpreter/interpreter-intrinsics.cc',
'../../src/interpreter/interpreter-intrinsics.h',
'../../src/interpreter/source-position-table.cc',
'../../src/interpreter/source-position-table.h',
'../../src/isolate-inl.h',