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/full-codegen.h"
20#include "src/parser.h"
21#include "src/rewriter.h"
22
23#include "test/cctest/compiler/c-signature.h"
24#include "test/cctest/compiler/function-tester.h"
25
26using namespace v8::internal;
27using namespace v8::internal::compiler;
28
29
30#if V8_TURBOFAN_TARGET
31
32typedef RawMachineAssembler::Label MLabel;
33
34static Handle<JSFunction> NewFunction(const char* source) {
35  return v8::Utils::OpenHandle(
36      *v8::Handle<v8::Function>::Cast(CompileRun(source)));
37}
38
39
40class DeoptCodegenTester {
41 public:
42  explicit DeoptCodegenTester(HandleAndZoneScope* scope, const char* src)
43      : scope_(scope),
44        function(NewFunction(src)),
45        info(function, scope->main_zone()),
46        bailout_id(-1) {
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));
52
53    DCHECK(info.shared_info()->has_deoptimization_support());
54
55    graph = new (scope_->main_zone()) Graph(scope_->main_zone());
56  }
57
58  virtual ~DeoptCodegenTester() { delete code; }
59
60  void GenerateCodeFromSchedule(Schedule* schedule) {
61    OFStream os(stdout);
62    if (FLAG_trace_turbo) {
63      os << *schedule;
64    }
65
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,
69                                                           schedule);
70    SourcePositionTable source_positions(graph);
71    InstructionSelector selector(code, &source_positions);
72    selector.SelectInstructions();
73
74    if (FLAG_trace_turbo) {
75      os << "----- Instruction sequence before register allocation -----\n"
76         << *code;
77    }
78
79    RegisterAllocator allocator(code);
80    CHECK(allocator.Allocate());
81
82    if (FLAG_trace_turbo) {
83      os << "----- Instruction sequence after register allocation -----\n"
84         << *code;
85    }
86
87    compiler::CodeGenerator generator(code);
88    result_code = generator.GenerateCode();
89
90#ifdef OBJECT_PRINT
91    if (FLAG_print_opt_code || FLAG_trace_turbo) {
92      result_code->Print();
93    }
94#endif
95  }
96
97  Zone* zone() { return scope_->main_zone(); }
98
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;
105  Graph* graph;
106};
107
108
109class TrivialDeoptCodegenTester : public DeoptCodegenTester {
110 public:
111  explicit TrivialDeoptCodegenTester(HandleAndZoneScope* scope)
112      : DeoptCodegenTester(scope,
113                           "function foo() { deopt(); return 42; }; foo") {}
114
115  void GenerateCode() {
116    GenerateCodeFromSchedule(BuildGraphAndSchedule(graph));
117  }
118
119  Schedule* BuildGraphAndSchedule(Graph* graph) {
120    CommonOperatorBuilder common(zone());
121
122    // Manually construct a schedule for the function below:
123    // function foo() {
124    //   deopt();
125    // }
126
127    CSignature1<Object*, Object*> sig;
128    RawMachineAssembler m(graph, &sig);
129
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));
135
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));
141
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));
146
147    Node* state_node = m.NewNode(
148        common.FrameState(JS_FRAME, bailout_id, kIgnoreOutput), parameters,
149        locals, stack, caller_context_node, m.UndefinedConstant());
150
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));
155
156    m.CallJS0(deopt_fun_node, m.UndefinedConstant(), context_node, state_node);
157
158    m.Return(m.UndefinedConstant());
159
160    // Schedule the graph:
161    Schedule* schedule = m.Export();
162
163    return schedule;
164  }
165
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();
172      }
173    }
174    CHECK(false);
175    return BailoutId(-1);
176  }
177};
178
179
180TEST(TurboTrivialDeoptCodegen) {
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  DeoptimizationInputData* data =
191      DeoptimizationInputData::cast(t.result_code->deoptimization_data());
192
193  // TODO(jarin) Find a way to test the safepoint.
194
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());
198}
199
200
201TEST(TurboTrivialDeoptCodegenAndRun) {
202  HandleAndZoneScope scope;
203  InitializedHandleScope handles;
204
205  FLAG_allow_natives_syntax = true;
206  FLAG_turbo_deoptimization = true;
207
208  TrivialDeoptCodegenTester t(&scope);
209  t.GenerateCode();
210
211  t.function->ReplaceCode(*t.result_code);
212  t.info.context()->native_context()->AddOptimizedCode(*t.result_code);
213
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)));
222}
223
224
225class TrivialRuntimeDeoptCodegenTester : public DeoptCodegenTester {
226 public:
227  explicit TrivialRuntimeDeoptCodegenTester(HandleAndZoneScope* scope)
228      : DeoptCodegenTester(
229            scope,
230            "function foo() { %DeoptimizeFunction(foo); return 42; }; foo") {}
231
232  void GenerateCode() {
233    GenerateCodeFromSchedule(BuildGraphAndSchedule(graph));
234  }
235
236  Schedule* BuildGraphAndSchedule(Graph* graph) {
237    CommonOperatorBuilder common(zone());
238
239    // Manually construct a schedule for the function below:
240    // function foo() {
241    //   %DeoptimizeFunction(foo);
242    // }
243
244    CSignature1<Object*, Object*> sig;
245    RawMachineAssembler m(graph, &sig);
246
247    Unique<Object> this_fun_constant =
248        Unique<Object>::CreateUninitialized(function);
249    Node* this_fun_node = m.NewNode(common.HeapConstant(this_fun_constant));
250
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));
255
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));
260
261    Node* state_node = m.NewNode(
262        common.FrameState(JS_FRAME, bailout_id, kIgnoreOutput), parameters,
263        locals, stack, context_node, m.UndefinedConstant());
264
265    m.CallRuntime1(Runtime::kDeoptimizeFunction, this_fun_node, context_node,
266                   state_node);
267
268    m.Return(m.UndefinedConstant());
269
270    // Schedule the graph:
271    Schedule* schedule = m.Export();
272
273    return schedule;
274  }
275
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();
282      }
283    }
284    CHECK(false);
285    return BailoutId(-1);
286  }
287};
288
289
290TEST(TurboTrivialRuntimeDeoptCodegenAndRun) {
291  HandleAndZoneScope scope;
292  InitializedHandleScope handles;
293
294  FLAG_allow_natives_syntax = true;
295  FLAG_turbo_deoptimization = true;
296
297  TrivialRuntimeDeoptCodegenTester t(&scope);
298  t.GenerateCode();
299
300  t.function->ReplaceCode(*t.result_code);
301  t.info.context()->native_context()->AddOptimizedCode(*t.result_code);
302
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)));
311}
312
313#endif
314