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