a90e4025dc82d2689aa43c1cde1c316a8a6addc7
[platform/upstream/nodejs.git] / deps / v8 / test / cctest / compiler / test-codegen-deopt.cc
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.
4
5 #include "src/v8.h"
6 #include "test/cctest/cctest.h"
7
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"
18
19 #include "src/ast-numbering.h"
20 #include "src/full-codegen.h"
21 #include "src/parser.h"
22 #include "src/rewriter.h"
23
24 #include "test/cctest/compiler/c-signature.h"
25 #include "test/cctest/compiler/function-tester.h"
26
27 using namespace v8::internal;
28 using namespace v8::internal::compiler;
29
30
31 #if V8_TURBOFAN_TARGET
32
33 typedef RawMachineAssembler::Label MLabel;
34 typedef v8::internal::compiler::InstructionSequence TestInstrSeq;
35
36 static Handle<JSFunction> NewFunction(const char* source) {
37   return v8::Utils::OpenHandle(
38       *v8::Handle<v8::Function>::Cast(CompileRun(source)));
39 }
40
41
42 class DeoptCodegenTester {
43  public:
44   explicit DeoptCodegenTester(HandleAndZoneScope* scope, const char* src)
45       : scope_(scope),
46         function(NewFunction(src)),
47         info(function, scope->main_zone()),
48         bailout_id(-1) {
49     CHECK(Parser::ParseStatic(&info));
50     info.SetOptimizing(BailoutId::None(), Handle<Code>(function->code()));
51     CHECK(Compiler::Analyze(&info));
52     CHECK(Compiler::EnsureDeoptimizationSupport(&info));
53
54     DCHECK(info.shared_info()->has_deoptimization_support());
55
56     graph = new (scope_->main_zone()) Graph(scope_->main_zone());
57   }
58
59   virtual ~DeoptCodegenTester() {}
60
61   void GenerateCodeFromSchedule(Schedule* schedule) {
62     OFStream os(stdout);
63     if (FLAG_trace_turbo) {
64       os << *schedule;
65     }
66     result_code = Pipeline::GenerateCodeForTesting(&info, graph, schedule);
67 #ifdef OBJECT_PRINT
68     if (FLAG_print_opt_code || FLAG_trace_turbo) {
69       result_code->Print();
70     }
71 #endif
72   }
73
74   Zone* zone() { return scope_->main_zone(); }
75   Isolate* isolate() { return scope_->main_isolate(); }
76
77   HandleAndZoneScope* scope_;
78   Handle<JSFunction> function;
79   CompilationInfo info;
80   BailoutId bailout_id;
81   Handle<Code> result_code;
82   TestInstrSeq* code;
83   Graph* graph;
84 };
85
86
87 class TrivialDeoptCodegenTester : public DeoptCodegenTester {
88  public:
89   explicit TrivialDeoptCodegenTester(HandleAndZoneScope* scope)
90       : DeoptCodegenTester(scope,
91                            "function foo() { deopt(); return 42; }; foo") {}
92
93   void GenerateCode() {
94     GenerateCodeFromSchedule(BuildGraphAndSchedule(graph));
95   }
96
97   Schedule* BuildGraphAndSchedule(Graph* graph) {
98     CommonOperatorBuilder common(zone());
99
100     // Manually construct a schedule for the function below:
101     // function foo() {
102     //   deopt();
103     // }
104
105     CSignature1<Object*, Object*> sig;
106     RawMachineAssembler m(isolate(), graph, &sig);
107
108     Handle<JSFunction> deopt_function =
109         NewFunction("function deopt() { %DeoptimizeFunction(foo); }; deopt");
110     Unique<JSFunction> deopt_fun_constant =
111         Unique<JSFunction>::CreateUninitialized(deopt_function);
112     Node* deopt_fun_node = m.NewNode(common.HeapConstant(deopt_fun_constant));
113
114     Handle<Context> caller_context(function->context(), CcTest::i_isolate());
115     Unique<Context> caller_context_constant =
116         Unique<Context>::CreateUninitialized(caller_context);
117     Node* caller_context_node =
118         m.NewNode(common.HeapConstant(caller_context_constant));
119
120     bailout_id = GetCallBailoutId();
121     Node* parameters = m.NewNode(common.StateValues(1), m.UndefinedConstant());
122     Node* locals = m.NewNode(common.StateValues(0));
123     Node* stack = m.NewNode(common.StateValues(0));
124
125     Node* state_node = m.NewNode(
126         common.FrameState(JS_FRAME, bailout_id,
127                           OutputFrameStateCombine::Ignore()),
128         parameters, locals, stack, caller_context_node, m.UndefinedConstant());
129
130     Handle<Context> context(deopt_function->context(), CcTest::i_isolate());
131     Unique<Context> context_constant =
132         Unique<Context>::CreateUninitialized(context);
133     Node* context_node = m.NewNode(common.HeapConstant(context_constant));
134
135     m.CallJS0(deopt_fun_node, m.UndefinedConstant(), context_node, state_node);
136
137     m.Return(m.UndefinedConstant());
138
139     // Schedule the graph:
140     Schedule* schedule = m.Export();
141
142     return schedule;
143   }
144
145   BailoutId GetCallBailoutId() {
146     ZoneList<Statement*>* body = info.function()->body();
147     for (int i = 0; i < body->length(); i++) {
148       if (body->at(i)->IsExpressionStatement() &&
149           body->at(i)->AsExpressionStatement()->expression()->IsCall()) {
150         return body->at(i)->AsExpressionStatement()->expression()->id();
151       }
152     }
153     CHECK(false);
154     return BailoutId(-1);
155   }
156 };
157
158
159 TEST(TurboTrivialDeoptCodegen) {
160   HandleAndZoneScope scope;
161   InitializedHandleScope handles;
162
163   FLAG_allow_natives_syntax = true;
164   FLAG_turbo_deoptimization = true;
165
166   TrivialDeoptCodegenTester t(&scope);
167   t.GenerateCode();
168
169   DeoptimizationInputData* data =
170       DeoptimizationInputData::cast(t.result_code->deoptimization_data());
171
172   // TODO(jarin) Find a way to test the safepoint.
173
174   // Check that we deoptimize to the right AST id.
175   CHECK_EQ(1, data->DeoptCount());
176   CHECK_EQ(t.bailout_id.ToInt(), data->AstId(0).ToInt());
177 }
178
179
180 TEST(TurboTrivialDeoptCodegenAndRun) {
181   HandleAndZoneScope scope;
182   InitializedHandleScope handles;
183
184   FLAG_allow_natives_syntax = true;
185   FLAG_turbo_deoptimization = true;
186
187   TrivialDeoptCodegenTester t(&scope);
188   t.GenerateCode();
189
190   t.function->ReplaceCode(*t.result_code);
191   t.info.context()->native_context()->AddOptimizedCode(*t.result_code);
192
193   Isolate* isolate = scope.main_isolate();
194   Handle<Object> result;
195   bool has_pending_exception =
196       !Execution::Call(isolate, t.function,
197                        isolate->factory()->undefined_value(), 0, NULL,
198                        false).ToHandle(&result);
199   CHECK(!has_pending_exception);
200   CHECK(result->SameValue(Smi::FromInt(42)));
201 }
202
203
204 class TrivialRuntimeDeoptCodegenTester : public DeoptCodegenTester {
205  public:
206   explicit TrivialRuntimeDeoptCodegenTester(HandleAndZoneScope* scope)
207       : DeoptCodegenTester(
208             scope,
209             "function foo() { %DeoptimizeFunction(foo); return 42; }; foo") {}
210
211   void GenerateCode() {
212     GenerateCodeFromSchedule(BuildGraphAndSchedule(graph));
213   }
214
215   Schedule* BuildGraphAndSchedule(Graph* graph) {
216     CommonOperatorBuilder common(zone());
217
218     // Manually construct a schedule for the function below:
219     // function foo() {
220     //   %DeoptimizeFunction(foo);
221     // }
222
223     CSignature1<Object*, Object*> sig;
224     RawMachineAssembler m(isolate(), graph, &sig);
225
226     Unique<HeapObject> this_fun_constant =
227         Unique<HeapObject>::CreateUninitialized(function);
228     Node* this_fun_node = m.NewNode(common.HeapConstant(this_fun_constant));
229
230     Handle<Context> context(function->context(), CcTest::i_isolate());
231     Unique<HeapObject> context_constant =
232         Unique<HeapObject>::CreateUninitialized(context);
233     Node* context_node = m.NewNode(common.HeapConstant(context_constant));
234
235     bailout_id = GetCallBailoutId();
236     Node* parameters = m.NewNode(common.StateValues(1), m.UndefinedConstant());
237     Node* locals = m.NewNode(common.StateValues(0));
238     Node* stack = m.NewNode(common.StateValues(0));
239
240     Node* state_node = m.NewNode(
241         common.FrameState(JS_FRAME, bailout_id,
242                           OutputFrameStateCombine::Ignore()),
243         parameters, locals, stack, context_node, m.UndefinedConstant());
244
245     m.CallRuntime1(Runtime::kDeoptimizeFunction, this_fun_node, context_node,
246                    state_node);
247
248     m.Return(m.UndefinedConstant());
249
250     // Schedule the graph:
251     Schedule* schedule = m.Export();
252
253     return schedule;
254   }
255
256   BailoutId GetCallBailoutId() {
257     ZoneList<Statement*>* body = info.function()->body();
258     for (int i = 0; i < body->length(); i++) {
259       if (body->at(i)->IsExpressionStatement() &&
260           body->at(i)->AsExpressionStatement()->expression()->IsCallRuntime()) {
261         return body->at(i)->AsExpressionStatement()->expression()->id();
262       }
263     }
264     CHECK(false);
265     return BailoutId(-1);
266   }
267 };
268
269
270 TEST(TurboTrivialRuntimeDeoptCodegenAndRun) {
271   HandleAndZoneScope scope;
272   InitializedHandleScope handles;
273
274   FLAG_allow_natives_syntax = true;
275   FLAG_turbo_deoptimization = true;
276
277   TrivialRuntimeDeoptCodegenTester t(&scope);
278   t.GenerateCode();
279
280   t.function->ReplaceCode(*t.result_code);
281   t.info.context()->native_context()->AddOptimizedCode(*t.result_code);
282
283   Isolate* isolate = scope.main_isolate();
284   Handle<Object> result;
285   bool has_pending_exception =
286       !Execution::Call(isolate, t.function,
287                        isolate->factory()->undefined_value(), 0, NULL,
288                        false).ToHandle(&result);
289   CHECK(!has_pending_exception);
290   CHECK(result->SameValue(Smi::FromInt(42)));
291 }
292
293 #endif