1 // Copyright 2014 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 #include "test/cctest/cctest.h"
8 #include "src/compiler/code-generator.h"
9 #include "src/compiler/common-operator.h"
10 #include "src/compiler/graph.h"
11 #include "src/compiler/instruction-selector.h"
12 #include "src/compiler/machine-operator.h"
13 #include "src/compiler/node.h"
14 #include "src/compiler/operator.h"
15 #include "src/compiler/raw-machine-assembler.h"
16 #include "src/compiler/register-allocator.h"
17 #include "src/compiler/schedule.h"
19 #include "src/full-codegen.h"
20 #include "src/parser.h"
21 #include "src/rewriter.h"
23 #include "test/cctest/compiler/c-signature.h"
24 #include "test/cctest/compiler/function-tester.h"
26 using namespace v8::internal;
27 using namespace v8::internal::compiler;
30 #if V8_TURBOFAN_TARGET
32 typedef RawMachineAssembler::Label MLabel;
34 static Handle<JSFunction> NewFunction(const char* source) {
35 return v8::Utils::OpenHandle(
36 *v8::Handle<v8::Function>::Cast(CompileRun(source)));
40 class DeoptCodegenTester {
42 explicit DeoptCodegenTester(HandleAndZoneScope* scope, const char* src)
44 function(NewFunction(src)),
45 info(function, scope->main_zone()),
47 CHECK(Parser::Parse(&info));
48 info.SetOptimizing(BailoutId::None(), Handle<Code>(function->code()));
49 CHECK(Rewriter::Rewrite(&info));
50 CHECK(Scope::Analyze(&info));
51 CHECK(Compiler::EnsureDeoptimizationSupport(&info));
53 DCHECK(info.shared_info()->has_deoptimization_support());
55 graph = new (scope_->main_zone()) Graph(scope_->main_zone());
58 virtual ~DeoptCodegenTester() { delete code; }
60 void GenerateCodeFromSchedule(Schedule* schedule) {
62 if (FLAG_trace_turbo) {
66 // Initialize the codegen and generate code.
67 Linkage* linkage = new (scope_->main_zone()) Linkage(&info);
68 code = new v8::internal::compiler::InstructionSequence(linkage, graph,
70 SourcePositionTable source_positions(graph);
71 InstructionSelector selector(code, &source_positions);
72 selector.SelectInstructions();
74 if (FLAG_trace_turbo) {
75 os << "----- Instruction sequence before register allocation -----\n"
79 RegisterAllocator allocator(code);
80 CHECK(allocator.Allocate());
82 if (FLAG_trace_turbo) {
83 os << "----- Instruction sequence after register allocation -----\n"
87 compiler::CodeGenerator generator(code);
88 result_code = generator.GenerateCode();
91 if (FLAG_print_opt_code || FLAG_trace_turbo) {
97 Zone* zone() { return scope_->main_zone(); }
99 HandleAndZoneScope* scope_;
100 Handle<JSFunction> function;
101 CompilationInfo info;
102 BailoutId bailout_id;
103 Handle<Code> result_code;
104 v8::internal::compiler::InstructionSequence* code;
109 class TrivialDeoptCodegenTester : public DeoptCodegenTester {
111 explicit TrivialDeoptCodegenTester(HandleAndZoneScope* scope)
112 : DeoptCodegenTester(scope,
113 "function foo() { deopt(); return 42; }; foo") {}
115 void GenerateCode() {
116 GenerateCodeFromSchedule(BuildGraphAndSchedule(graph));
119 Schedule* BuildGraphAndSchedule(Graph* graph) {
120 CommonOperatorBuilder common(zone());
122 // Manually construct a schedule for the function below:
127 CSignature1<Object*, Object*> sig;
128 RawMachineAssembler m(graph, &sig);
130 Handle<JSFunction> deopt_function =
131 NewFunction("function deopt() { %DeoptimizeFunction(foo); }; deopt");
132 Unique<Object> deopt_fun_constant =
133 Unique<Object>::CreateUninitialized(deopt_function);
134 Node* deopt_fun_node = m.NewNode(common.HeapConstant(deopt_fun_constant));
136 Handle<Context> caller_context(function->context(), CcTest::i_isolate());
137 Unique<Object> caller_context_constant =
138 Unique<Object>::CreateUninitialized(caller_context);
139 Node* caller_context_node =
140 m.NewNode(common.HeapConstant(caller_context_constant));
142 bailout_id = GetCallBailoutId();
143 Node* parameters = m.NewNode(common.StateValues(1), m.UndefinedConstant());
144 Node* locals = m.NewNode(common.StateValues(0));
145 Node* stack = m.NewNode(common.StateValues(0));
147 Node* state_node = m.NewNode(
148 common.FrameState(JS_FRAME, bailout_id, kIgnoreOutput), parameters,
149 locals, stack, caller_context_node, m.UndefinedConstant());
151 Handle<Context> context(deopt_function->context(), CcTest::i_isolate());
152 Unique<Object> context_constant =
153 Unique<Object>::CreateUninitialized(context);
154 Node* context_node = m.NewNode(common.HeapConstant(context_constant));
156 m.CallJS0(deopt_fun_node, m.UndefinedConstant(), context_node, state_node);
158 m.Return(m.UndefinedConstant());
160 // Schedule the graph:
161 Schedule* schedule = m.Export();
166 BailoutId GetCallBailoutId() {
167 ZoneList<Statement*>* body = info.function()->body();
168 for (int i = 0; i < body->length(); i++) {
169 if (body->at(i)->IsExpressionStatement() &&
170 body->at(i)->AsExpressionStatement()->expression()->IsCall()) {
171 return body->at(i)->AsExpressionStatement()->expression()->id();
175 return BailoutId(-1);
180 TEST(TurboTrivialDeoptCodegen) {
181 HandleAndZoneScope scope;
182 InitializedHandleScope handles;
184 FLAG_allow_natives_syntax = true;
185 FLAG_turbo_deoptimization = true;
187 TrivialDeoptCodegenTester t(&scope);
190 DeoptimizationInputData* data =
191 DeoptimizationInputData::cast(t.result_code->deoptimization_data());
193 // TODO(jarin) Find a way to test the safepoint.
195 // Check that we deoptimize to the right AST id.
196 CHECK_EQ(1, data->DeoptCount());
197 CHECK_EQ(t.bailout_id.ToInt(), data->AstId(0).ToInt());
201 TEST(TurboTrivialDeoptCodegenAndRun) {
202 HandleAndZoneScope scope;
203 InitializedHandleScope handles;
205 FLAG_allow_natives_syntax = true;
206 FLAG_turbo_deoptimization = true;
208 TrivialDeoptCodegenTester t(&scope);
211 t.function->ReplaceCode(*t.result_code);
212 t.info.context()->native_context()->AddOptimizedCode(*t.result_code);
214 Isolate* isolate = scope.main_isolate();
215 Handle<Object> result;
216 bool has_pending_exception =
217 !Execution::Call(isolate, t.function,
218 isolate->factory()->undefined_value(), 0, NULL,
219 false).ToHandle(&result);
220 CHECK(!has_pending_exception);
221 CHECK(result->SameValue(Smi::FromInt(42)));
225 class TrivialRuntimeDeoptCodegenTester : public DeoptCodegenTester {
227 explicit TrivialRuntimeDeoptCodegenTester(HandleAndZoneScope* scope)
228 : DeoptCodegenTester(
230 "function foo() { %DeoptimizeFunction(foo); return 42; }; foo") {}
232 void GenerateCode() {
233 GenerateCodeFromSchedule(BuildGraphAndSchedule(graph));
236 Schedule* BuildGraphAndSchedule(Graph* graph) {
237 CommonOperatorBuilder common(zone());
239 // Manually construct a schedule for the function below:
241 // %DeoptimizeFunction(foo);
244 CSignature1<Object*, Object*> sig;
245 RawMachineAssembler m(graph, &sig);
247 Unique<Object> this_fun_constant =
248 Unique<Object>::CreateUninitialized(function);
249 Node* this_fun_node = m.NewNode(common.HeapConstant(this_fun_constant));
251 Handle<Context> context(function->context(), CcTest::i_isolate());
252 Unique<Object> context_constant =
253 Unique<Object>::CreateUninitialized(context);
254 Node* context_node = m.NewNode(common.HeapConstant(context_constant));
256 bailout_id = GetCallBailoutId();
257 Node* parameters = m.NewNode(common.StateValues(1), m.UndefinedConstant());
258 Node* locals = m.NewNode(common.StateValues(0));
259 Node* stack = m.NewNode(common.StateValues(0));
261 Node* state_node = m.NewNode(
262 common.FrameState(JS_FRAME, bailout_id, kIgnoreOutput), parameters,
263 locals, stack, context_node, m.UndefinedConstant());
265 m.CallRuntime1(Runtime::kDeoptimizeFunction, this_fun_node, context_node,
268 m.Return(m.UndefinedConstant());
270 // Schedule the graph:
271 Schedule* schedule = m.Export();
276 BailoutId GetCallBailoutId() {
277 ZoneList<Statement*>* body = info.function()->body();
278 for (int i = 0; i < body->length(); i++) {
279 if (body->at(i)->IsExpressionStatement() &&
280 body->at(i)->AsExpressionStatement()->expression()->IsCallRuntime()) {
281 return body->at(i)->AsExpressionStatement()->expression()->id();
285 return BailoutId(-1);
290 TEST(TurboTrivialRuntimeDeoptCodegenAndRun) {
291 HandleAndZoneScope scope;
292 InitializedHandleScope handles;
294 FLAG_allow_natives_syntax = true;
295 FLAG_turbo_deoptimization = true;
297 TrivialRuntimeDeoptCodegenTester t(&scope);
300 t.function->ReplaceCode(*t.result_code);
301 t.info.context()->native_context()->AddOptimizedCode(*t.result_code);
303 Isolate* isolate = scope.main_isolate();
304 Handle<Object> result;
305 bool has_pending_exception =
306 !Execution::Call(isolate, t.function,
307 isolate->factory()->undefined_value(), 0, NULL,
308 false).ToHandle(&result);
309 CHECK(!has_pending_exception);
310 CHECK(result->SameValue(Smi::FromInt(42)));