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
26 using namespace v8::internal;
27 using namespace v8::internal::compiler;
28
29
30 #if V8_TURBOFAN_TARGET
31
32 typedef RawMachineAssembler::Label MLabel;
33
NewFunction(const char * source)34 static Handle<JSFunction> NewFunction(const char* source) {
35 return v8::Utils::OpenHandle(
36 *v8::Handle<v8::Function>::Cast(CompileRun(source)));
37 }
38
39
40 class DeoptCodegenTester {
41 public:
DeoptCodegenTester(HandleAndZoneScope * scope,const char * src)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
~DeoptCodegenTester()58 virtual ~DeoptCodegenTester() { delete code; }
59
GenerateCodeFromSchedule(Schedule * schedule)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
zone()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
109 class TrivialDeoptCodegenTester : public DeoptCodegenTester {
110 public:
TrivialDeoptCodegenTester(HandleAndZoneScope * scope)111 explicit TrivialDeoptCodegenTester(HandleAndZoneScope* scope)
112 : DeoptCodegenTester(scope,
113 "function foo() { deopt(); return 42; }; foo") {}
114
GenerateCode()115 void GenerateCode() {
116 GenerateCodeFromSchedule(BuildGraphAndSchedule(graph));
117 }
118
BuildGraphAndSchedule(Graph * graph)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
GetCallBailoutId()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
TEST(TurboTrivialDeoptCodegen)180 TEST(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
TEST(TurboTrivialDeoptCodegenAndRun)201 TEST(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
225 class TrivialRuntimeDeoptCodegenTester : public DeoptCodegenTester {
226 public:
TrivialRuntimeDeoptCodegenTester(HandleAndZoneScope * scope)227 explicit TrivialRuntimeDeoptCodegenTester(HandleAndZoneScope* scope)
228 : DeoptCodegenTester(
229 scope,
230 "function foo() { %DeoptimizeFunction(foo); return 42; }; foo") {}
231
GenerateCode()232 void GenerateCode() {
233 GenerateCodeFromSchedule(BuildGraphAndSchedule(graph));
234 }
235
BuildGraphAndSchedule(Graph * graph)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
GetCallBailoutId()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
TEST(TurboTrivialRuntimeDeoptCodegenAndRun)290 TEST(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