2014-07-30 13:54:45 +00:00
|
|
|
// Copyright 2014 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 "test/cctest/cctest.h"
|
|
|
|
|
|
|
|
#include "src/compiler/code-generator.h"
|
|
|
|
#include "src/compiler/common-operator.h"
|
|
|
|
#include "src/compiler/graph.h"
|
|
|
|
#include "src/compiler/instruction-selector.h"
|
|
|
|
#include "src/compiler/machine-operator.h"
|
|
|
|
#include "src/compiler/node.h"
|
|
|
|
#include "src/compiler/operator.h"
|
|
|
|
#include "src/compiler/raw-machine-assembler.h"
|
|
|
|
#include "src/compiler/register-allocator.h"
|
|
|
|
#include "src/compiler/schedule.h"
|
|
|
|
|
2014-10-21 12:16:37 +00:00
|
|
|
#include "src/ast-numbering.h"
|
2014-07-30 13:54:45 +00:00
|
|
|
#include "src/full-codegen.h"
|
|
|
|
#include "src/parser.h"
|
|
|
|
#include "src/rewriter.h"
|
|
|
|
|
2014-09-03 10:13:21 +00:00
|
|
|
#include "test/cctest/compiler/c-signature.h"
|
2014-07-30 13:54:45 +00:00
|
|
|
#include "test/cctest/compiler/function-tester.h"
|
|
|
|
|
|
|
|
using namespace v8::internal;
|
|
|
|
using namespace v8::internal::compiler;
|
|
|
|
|
2014-07-31 11:59:49 +00:00
|
|
|
|
|
|
|
#if V8_TURBOFAN_TARGET
|
|
|
|
|
2014-07-30 13:54:45 +00:00
|
|
|
typedef RawMachineAssembler::Label MLabel;
|
2014-11-03 13:41:56 +00:00
|
|
|
typedef v8::internal::compiler::InstructionSequence TestInstrSeq;
|
2014-07-30 13:54:45 +00:00
|
|
|
|
|
|
|
static Handle<JSFunction> NewFunction(const char* source) {
|
|
|
|
return v8::Utils::OpenHandle(
|
|
|
|
*v8::Handle<v8::Function>::Cast(CompileRun(source)));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class DeoptCodegenTester {
|
|
|
|
public:
|
|
|
|
explicit DeoptCodegenTester(HandleAndZoneScope* scope, const char* src)
|
|
|
|
: scope_(scope),
|
|
|
|
function(NewFunction(src)),
|
|
|
|
info(function, scope->main_zone()),
|
|
|
|
bailout_id(-1) {
|
|
|
|
CHECK(Parser::Parse(&info));
|
|
|
|
info.SetOptimizing(BailoutId::None(), Handle<Code>(function->code()));
|
2014-10-28 13:23:54 +00:00
|
|
|
CHECK(Compiler::Analyze(&info));
|
2014-09-18 09:02:36 +00:00
|
|
|
CHECK(Compiler::EnsureDeoptimizationSupport(&info));
|
2014-07-30 13:54:45 +00:00
|
|
|
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(info.shared_info()->has_deoptimization_support());
|
2014-07-30 13:54:45 +00:00
|
|
|
|
|
|
|
graph = new (scope_->main_zone()) Graph(scope_->main_zone());
|
|
|
|
}
|
|
|
|
|
2014-11-14 16:44:38 +00:00
|
|
|
virtual ~DeoptCodegenTester() {}
|
2014-07-30 13:54:45 +00:00
|
|
|
|
|
|
|
void GenerateCodeFromSchedule(Schedule* schedule) {
|
|
|
|
OFStream os(stdout);
|
2014-09-03 10:13:21 +00:00
|
|
|
if (FLAG_trace_turbo) {
|
|
|
|
os << *schedule;
|
|
|
|
}
|
2014-11-17 14:46:41 +00:00
|
|
|
result_code = Pipeline::GenerateCodeForTesting(&info, graph, schedule);
|
2014-09-03 10:13:21 +00:00
|
|
|
#ifdef OBJECT_PRINT
|
|
|
|
if (FLAG_print_opt_code || FLAG_trace_turbo) {
|
|
|
|
result_code->Print();
|
|
|
|
}
|
2014-07-30 13:54:45 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
Zone* zone() { return scope_->main_zone(); }
|
|
|
|
|
|
|
|
HandleAndZoneScope* scope_;
|
|
|
|
Handle<JSFunction> function;
|
|
|
|
CompilationInfo info;
|
|
|
|
BailoutId bailout_id;
|
|
|
|
Handle<Code> result_code;
|
2014-11-03 13:41:56 +00:00
|
|
|
TestInstrSeq* code;
|
2014-07-30 13:54:45 +00:00
|
|
|
Graph* graph;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class TrivialDeoptCodegenTester : public DeoptCodegenTester {
|
|
|
|
public:
|
|
|
|
explicit TrivialDeoptCodegenTester(HandleAndZoneScope* scope)
|
|
|
|
: DeoptCodegenTester(scope,
|
|
|
|
"function foo() { deopt(); return 42; }; foo") {}
|
|
|
|
|
|
|
|
void GenerateCode() {
|
|
|
|
GenerateCodeFromSchedule(BuildGraphAndSchedule(graph));
|
|
|
|
}
|
|
|
|
|
|
|
|
Schedule* BuildGraphAndSchedule(Graph* graph) {
|
|
|
|
CommonOperatorBuilder common(zone());
|
|
|
|
|
|
|
|
// Manually construct a schedule for the function below:
|
|
|
|
// function foo() {
|
|
|
|
// deopt();
|
|
|
|
// }
|
|
|
|
|
2014-09-03 10:13:21 +00:00
|
|
|
CSignature1<Object*, Object*> sig;
|
|
|
|
RawMachineAssembler m(graph, &sig);
|
2014-07-30 13:54:45 +00:00
|
|
|
|
|
|
|
Handle<JSFunction> deopt_function =
|
|
|
|
NewFunction("function deopt() { %DeoptimizeFunction(foo); }; deopt");
|
2014-10-07 13:30:28 +00:00
|
|
|
Unique<JSFunction> deopt_fun_constant =
|
|
|
|
Unique<JSFunction>::CreateUninitialized(deopt_function);
|
2014-07-30 13:54:45 +00:00
|
|
|
Node* deopt_fun_node = m.NewNode(common.HeapConstant(deopt_fun_constant));
|
|
|
|
|
2014-09-04 14:52:52 +00:00
|
|
|
Handle<Context> caller_context(function->context(), CcTest::i_isolate());
|
2014-10-07 13:30:28 +00:00
|
|
|
Unique<Context> caller_context_constant =
|
|
|
|
Unique<Context>::CreateUninitialized(caller_context);
|
2014-09-04 14:52:52 +00:00
|
|
|
Node* caller_context_node =
|
|
|
|
m.NewNode(common.HeapConstant(caller_context_constant));
|
2014-07-30 13:54:45 +00:00
|
|
|
|
|
|
|
bailout_id = GetCallBailoutId();
|
2014-09-03 14:10:20 +00:00
|
|
|
Node* parameters = m.NewNode(common.StateValues(1), m.UndefinedConstant());
|
2014-08-06 11:49:02 +00:00
|
|
|
Node* locals = m.NewNode(common.StateValues(0));
|
|
|
|
Node* stack = m.NewNode(common.StateValues(0));
|
|
|
|
|
2014-09-18 08:56:52 +00:00
|
|
|
Node* state_node = m.NewNode(
|
2014-09-29 13:37:58 +00:00
|
|
|
common.FrameState(JS_FRAME, bailout_id,
|
|
|
|
OutputFrameStateCombine::Ignore()),
|
|
|
|
parameters, locals, stack, caller_context_node, m.UndefinedConstant());
|
2014-09-04 14:52:52 +00:00
|
|
|
|
|
|
|
Handle<Context> context(deopt_function->context(), CcTest::i_isolate());
|
2014-10-07 13:30:28 +00:00
|
|
|
Unique<Context> context_constant =
|
|
|
|
Unique<Context>::CreateUninitialized(context);
|
2014-09-04 14:52:52 +00:00
|
|
|
Node* context_node = m.NewNode(common.HeapConstant(context_constant));
|
2014-09-01 09:31:14 +00:00
|
|
|
|
2014-09-03 14:10:20 +00:00
|
|
|
m.CallJS0(deopt_fun_node, m.UndefinedConstant(), context_node, state_node);
|
2014-09-01 09:31:14 +00:00
|
|
|
|
2014-09-03 14:10:20 +00:00
|
|
|
m.Return(m.UndefinedConstant());
|
2014-07-30 13:54:45 +00:00
|
|
|
|
|
|
|
// Schedule the graph:
|
|
|
|
Schedule* schedule = m.Export();
|
|
|
|
|
|
|
|
return schedule;
|
|
|
|
}
|
|
|
|
|
|
|
|
BailoutId GetCallBailoutId() {
|
|
|
|
ZoneList<Statement*>* body = info.function()->body();
|
|
|
|
for (int i = 0; i < body->length(); i++) {
|
|
|
|
if (body->at(i)->IsExpressionStatement() &&
|
|
|
|
body->at(i)->AsExpressionStatement()->expression()->IsCall()) {
|
|
|
|
return body->at(i)->AsExpressionStatement()->expression()->id();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CHECK(false);
|
|
|
|
return BailoutId(-1);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
TEST(TurboTrivialDeoptCodegen) {
|
|
|
|
HandleAndZoneScope scope;
|
|
|
|
InitializedHandleScope handles;
|
|
|
|
|
|
|
|
FLAG_allow_natives_syntax = true;
|
|
|
|
FLAG_turbo_deoptimization = true;
|
|
|
|
|
|
|
|
TrivialDeoptCodegenTester t(&scope);
|
|
|
|
t.GenerateCode();
|
|
|
|
|
|
|
|
DeoptimizationInputData* data =
|
|
|
|
DeoptimizationInputData::cast(t.result_code->deoptimization_data());
|
|
|
|
|
2014-09-01 09:31:14 +00:00
|
|
|
// TODO(jarin) Find a way to test the safepoint.
|
2014-07-30 13:54:45 +00:00
|
|
|
|
|
|
|
// Check that we deoptimize to the right AST id.
|
|
|
|
CHECK_EQ(1, data->DeoptCount());
|
|
|
|
CHECK_EQ(t.bailout_id.ToInt(), data->AstId(0).ToInt());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TEST(TurboTrivialDeoptCodegenAndRun) {
|
|
|
|
HandleAndZoneScope scope;
|
|
|
|
InitializedHandleScope handles;
|
|
|
|
|
|
|
|
FLAG_allow_natives_syntax = true;
|
|
|
|
FLAG_turbo_deoptimization = true;
|
|
|
|
|
|
|
|
TrivialDeoptCodegenTester t(&scope);
|
|
|
|
t.GenerateCode();
|
|
|
|
|
|
|
|
t.function->ReplaceCode(*t.result_code);
|
|
|
|
t.info.context()->native_context()->AddOptimizedCode(*t.result_code);
|
|
|
|
|
|
|
|
Isolate* isolate = scope.main_isolate();
|
|
|
|
Handle<Object> result;
|
|
|
|
bool has_pending_exception =
|
|
|
|
!Execution::Call(isolate, t.function,
|
|
|
|
isolate->factory()->undefined_value(), 0, NULL,
|
|
|
|
false).ToHandle(&result);
|
|
|
|
CHECK(!has_pending_exception);
|
|
|
|
CHECK(result->SameValue(Smi::FromInt(42)));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class TrivialRuntimeDeoptCodegenTester : public DeoptCodegenTester {
|
|
|
|
public:
|
|
|
|
explicit TrivialRuntimeDeoptCodegenTester(HandleAndZoneScope* scope)
|
|
|
|
: DeoptCodegenTester(
|
|
|
|
scope,
|
|
|
|
"function foo() { %DeoptimizeFunction(foo); return 42; }; foo") {}
|
|
|
|
|
|
|
|
void GenerateCode() {
|
|
|
|
GenerateCodeFromSchedule(BuildGraphAndSchedule(graph));
|
|
|
|
}
|
|
|
|
|
|
|
|
Schedule* BuildGraphAndSchedule(Graph* graph) {
|
|
|
|
CommonOperatorBuilder common(zone());
|
|
|
|
|
|
|
|
// Manually construct a schedule for the function below:
|
|
|
|
// function foo() {
|
|
|
|
// %DeoptimizeFunction(foo);
|
|
|
|
// }
|
|
|
|
|
2014-09-03 10:13:21 +00:00
|
|
|
CSignature1<Object*, Object*> sig;
|
|
|
|
RawMachineAssembler m(graph, &sig);
|
2014-07-30 13:54:45 +00:00
|
|
|
|
2014-10-07 13:30:28 +00:00
|
|
|
Unique<HeapObject> this_fun_constant =
|
|
|
|
Unique<HeapObject>::CreateUninitialized(function);
|
2014-07-30 13:54:45 +00:00
|
|
|
Node* this_fun_node = m.NewNode(common.HeapConstant(this_fun_constant));
|
|
|
|
|
2014-09-03 14:10:20 +00:00
|
|
|
Handle<Context> context(function->context(), CcTest::i_isolate());
|
2014-10-07 13:30:28 +00:00
|
|
|
Unique<HeapObject> context_constant =
|
|
|
|
Unique<HeapObject>::CreateUninitialized(context);
|
2014-09-03 10:13:21 +00:00
|
|
|
Node* context_node = m.NewNode(common.HeapConstant(context_constant));
|
|
|
|
|
2014-07-30 13:54:45 +00:00
|
|
|
bailout_id = GetCallBailoutId();
|
2014-09-03 14:10:20 +00:00
|
|
|
Node* parameters = m.NewNode(common.StateValues(1), m.UndefinedConstant());
|
2014-08-06 11:49:02 +00:00
|
|
|
Node* locals = m.NewNode(common.StateValues(0));
|
|
|
|
Node* stack = m.NewNode(common.StateValues(0));
|
|
|
|
|
2014-09-18 08:56:52 +00:00
|
|
|
Node* state_node = m.NewNode(
|
2014-09-29 13:37:58 +00:00
|
|
|
common.FrameState(JS_FRAME, bailout_id,
|
|
|
|
OutputFrameStateCombine::Ignore()),
|
|
|
|
parameters, locals, stack, context_node, m.UndefinedConstant());
|
2014-09-01 09:31:14 +00:00
|
|
|
|
2014-09-03 10:13:21 +00:00
|
|
|
m.CallRuntime1(Runtime::kDeoptimizeFunction, this_fun_node, context_node,
|
|
|
|
state_node);
|
2014-09-01 09:31:14 +00:00
|
|
|
|
2014-09-03 14:10:20 +00:00
|
|
|
m.Return(m.UndefinedConstant());
|
2014-07-30 13:54:45 +00:00
|
|
|
|
|
|
|
// Schedule the graph:
|
|
|
|
Schedule* schedule = m.Export();
|
|
|
|
|
|
|
|
return schedule;
|
|
|
|
}
|
|
|
|
|
|
|
|
BailoutId GetCallBailoutId() {
|
|
|
|
ZoneList<Statement*>* body = info.function()->body();
|
|
|
|
for (int i = 0; i < body->length(); i++) {
|
|
|
|
if (body->at(i)->IsExpressionStatement() &&
|
|
|
|
body->at(i)->AsExpressionStatement()->expression()->IsCallRuntime()) {
|
|
|
|
return body->at(i)->AsExpressionStatement()->expression()->id();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CHECK(false);
|
|
|
|
return BailoutId(-1);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
TEST(TurboTrivialRuntimeDeoptCodegenAndRun) {
|
|
|
|
HandleAndZoneScope scope;
|
|
|
|
InitializedHandleScope handles;
|
|
|
|
|
|
|
|
FLAG_allow_natives_syntax = true;
|
|
|
|
FLAG_turbo_deoptimization = true;
|
|
|
|
|
|
|
|
TrivialRuntimeDeoptCodegenTester t(&scope);
|
|
|
|
t.GenerateCode();
|
|
|
|
|
|
|
|
t.function->ReplaceCode(*t.result_code);
|
|
|
|
t.info.context()->native_context()->AddOptimizedCode(*t.result_code);
|
|
|
|
|
|
|
|
Isolate* isolate = scope.main_isolate();
|
|
|
|
Handle<Object> result;
|
|
|
|
bool has_pending_exception =
|
|
|
|
!Execution::Call(isolate, t.function,
|
|
|
|
isolate->factory()->undefined_value(), 0, NULL,
|
|
|
|
false).ToHandle(&result);
|
|
|
|
CHECK(!has_pending_exception);
|
|
|
|
CHECK(result->SameValue(Smi::FromInt(42)));
|
|
|
|
}
|
2014-07-31 11:59:49 +00:00
|
|
|
|
|
|
|
#endif
|