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/ast-numbering.h"
20 #include "src/full-codegen.h"
21 #include "src/parser.h"
22 #include "src/rewriter.h"
24 #include "test/cctest/compiler/c-signature.h"
25 #include "test/cctest/compiler/function-tester.h"
27 using namespace v8::internal;
28 using namespace v8::internal::compiler;
31 #if V8_TURBOFAN_TARGET
33 typedef RawMachineAssembler::Label MLabel;
34 typedef v8::internal::compiler::InstructionSequence TestInstrSeq;
36 static Handle<JSFunction> NewFunction(const char* source) {
37 return v8::Utils::OpenHandle(
38 *v8::Handle<v8::Function>::Cast(CompileRun(source)));
42 class DeoptCodegenTester {
44 explicit DeoptCodegenTester(HandleAndZoneScope* scope, const char* src)
46 function(NewFunction(src)),
47 info(function, scope->main_zone()),
49 CHECK(Parser::ParseStatic(&info));
50 info.SetOptimizing(BailoutId::None(), Handle<Code>(function->code()));
51 CHECK(Compiler::Analyze(&info));
52 CHECK(Compiler::EnsureDeoptimizationSupport(&info));
54 DCHECK(info.shared_info()->has_deoptimization_support());
56 graph = new (scope_->main_zone()) Graph(scope_->main_zone());
59 virtual ~DeoptCodegenTester() {}
61 void GenerateCodeFromSchedule(Schedule* schedule) {
63 if (FLAG_trace_turbo) {
66 result_code = Pipeline::GenerateCodeForTesting(&info, graph, schedule);
68 if (FLAG_print_opt_code || FLAG_trace_turbo) {
74 Zone* zone() { return scope_->main_zone(); }
75 Isolate* isolate() { return scope_->main_isolate(); }
77 HandleAndZoneScope* scope_;
78 Handle<JSFunction> function;
81 Handle<Code> result_code;
87 class TrivialDeoptCodegenTester : public DeoptCodegenTester {
89 explicit TrivialDeoptCodegenTester(HandleAndZoneScope* scope)
90 : DeoptCodegenTester(scope,
91 "function foo() { deopt(); return 42; }; foo") {}
94 GenerateCodeFromSchedule(BuildGraphAndSchedule(graph));
97 Schedule* BuildGraphAndSchedule(Graph* graph) {
98 CommonOperatorBuilder common(zone());
100 // Manually construct a schedule for the function below:
105 CSignature1<Object*, Object*> sig;
106 RawMachineAssembler m(isolate(), graph, &sig);
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));
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));
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));
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());
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));
135 m.CallJS0(deopt_fun_node, m.UndefinedConstant(), context_node, state_node);
137 m.Return(m.UndefinedConstant());
139 // Schedule the graph:
140 Schedule* schedule = m.Export();
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();
154 return BailoutId(-1);
159 TEST(TurboTrivialDeoptCodegen) {
160 HandleAndZoneScope scope;
161 InitializedHandleScope handles;
163 FLAG_allow_natives_syntax = true;
164 FLAG_turbo_deoptimization = true;
166 TrivialDeoptCodegenTester t(&scope);
169 DeoptimizationInputData* data =
170 DeoptimizationInputData::cast(t.result_code->deoptimization_data());
172 // TODO(jarin) Find a way to test the safepoint.
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());
180 TEST(TurboTrivialDeoptCodegenAndRun) {
181 HandleAndZoneScope scope;
182 InitializedHandleScope handles;
184 FLAG_allow_natives_syntax = true;
185 FLAG_turbo_deoptimization = true;
187 TrivialDeoptCodegenTester t(&scope);
190 t.function->ReplaceCode(*t.result_code);
191 t.info.context()->native_context()->AddOptimizedCode(*t.result_code);
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)));
204 class TrivialRuntimeDeoptCodegenTester : public DeoptCodegenTester {
206 explicit TrivialRuntimeDeoptCodegenTester(HandleAndZoneScope* scope)
207 : DeoptCodegenTester(
209 "function foo() { %DeoptimizeFunction(foo); return 42; }; foo") {}
211 void GenerateCode() {
212 GenerateCodeFromSchedule(BuildGraphAndSchedule(graph));
215 Schedule* BuildGraphAndSchedule(Graph* graph) {
216 CommonOperatorBuilder common(zone());
218 // Manually construct a schedule for the function below:
220 // %DeoptimizeFunction(foo);
223 CSignature1<Object*, Object*> sig;
224 RawMachineAssembler m(isolate(), graph, &sig);
226 Unique<HeapObject> this_fun_constant =
227 Unique<HeapObject>::CreateUninitialized(function);
228 Node* this_fun_node = m.NewNode(common.HeapConstant(this_fun_constant));
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));
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));
240 Node* state_node = m.NewNode(
241 common.FrameState(JS_FRAME, bailout_id,
242 OutputFrameStateCombine::Ignore()),
243 parameters, locals, stack, context_node, m.UndefinedConstant());
245 m.CallRuntime1(Runtime::kDeoptimizeFunction, this_fun_node, context_node,
248 m.Return(m.UndefinedConstant());
250 // Schedule the graph:
251 Schedule* schedule = m.Export();
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();
265 return BailoutId(-1);
270 TEST(TurboTrivialRuntimeDeoptCodegenAndRun) {
271 HandleAndZoneScope scope;
272 InitializedHandleScope handles;
274 FLAG_allow_natives_syntax = true;
275 FLAG_turbo_deoptimization = true;
277 TrivialRuntimeDeoptCodegenTester t(&scope);
280 t.function->ReplaceCode(*t.result_code);
281 t.info.context()->native_context()->AddOptimizedCode(*t.result_code);
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)));