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::Parse(&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() { delete code; }
61 void GenerateCodeFromSchedule(Schedule* schedule) {
63 if (FLAG_trace_turbo) {
67 // Initialize the codegen and generate code.
68 Linkage* linkage = new (scope_->main_zone()) Linkage(info.zone(), &info);
69 InstructionBlocks* instruction_blocks =
70 TestInstrSeq::InstructionBlocksFor(scope_->main_zone(), schedule);
71 code = new TestInstrSeq(scope_->main_zone(), instruction_blocks);
72 SourcePositionTable source_positions(graph);
73 InstructionSelector selector(scope_->main_zone(), graph, linkage, code,
74 schedule, &source_positions);
75 selector.SelectInstructions();
77 if (FLAG_trace_turbo) {
78 PrintableInstructionSequence printable = {
79 RegisterConfiguration::ArchDefault(), code};
80 os << "----- Instruction sequence before register allocation -----\n"
85 RegisterAllocator allocator(RegisterConfiguration::ArchDefault(),
86 scope_->main_zone(), &frame, code);
87 CHECK(allocator.Allocate());
89 if (FLAG_trace_turbo) {
90 PrintableInstructionSequence printable = {
91 RegisterConfiguration::ArchDefault(), code};
92 os << "----- Instruction sequence after register allocation -----\n"
96 compiler::CodeGenerator generator(&frame, linkage, code, &info);
97 result_code = generator.GenerateCode();
100 if (FLAG_print_opt_code || FLAG_trace_turbo) {
101 result_code->Print();
106 Zone* zone() { return scope_->main_zone(); }
108 HandleAndZoneScope* scope_;
109 Handle<JSFunction> function;
110 CompilationInfo info;
111 BailoutId bailout_id;
112 Handle<Code> result_code;
118 class TrivialDeoptCodegenTester : public DeoptCodegenTester {
120 explicit TrivialDeoptCodegenTester(HandleAndZoneScope* scope)
121 : DeoptCodegenTester(scope,
122 "function foo() { deopt(); return 42; }; foo") {}
124 void GenerateCode() {
125 GenerateCodeFromSchedule(BuildGraphAndSchedule(graph));
128 Schedule* BuildGraphAndSchedule(Graph* graph) {
129 CommonOperatorBuilder common(zone());
131 // Manually construct a schedule for the function below:
136 CSignature1<Object*, Object*> sig;
137 RawMachineAssembler m(graph, &sig);
139 Handle<JSFunction> deopt_function =
140 NewFunction("function deopt() { %DeoptimizeFunction(foo); }; deopt");
141 Unique<JSFunction> deopt_fun_constant =
142 Unique<JSFunction>::CreateUninitialized(deopt_function);
143 Node* deopt_fun_node = m.NewNode(common.HeapConstant(deopt_fun_constant));
145 Handle<Context> caller_context(function->context(), CcTest::i_isolate());
146 Unique<Context> caller_context_constant =
147 Unique<Context>::CreateUninitialized(caller_context);
148 Node* caller_context_node =
149 m.NewNode(common.HeapConstant(caller_context_constant));
151 bailout_id = GetCallBailoutId();
152 Node* parameters = m.NewNode(common.StateValues(1), m.UndefinedConstant());
153 Node* locals = m.NewNode(common.StateValues(0));
154 Node* stack = m.NewNode(common.StateValues(0));
156 Node* state_node = m.NewNode(
157 common.FrameState(JS_FRAME, bailout_id,
158 OutputFrameStateCombine::Ignore()),
159 parameters, locals, stack, caller_context_node, m.UndefinedConstant());
161 Handle<Context> context(deopt_function->context(), CcTest::i_isolate());
162 Unique<Context> context_constant =
163 Unique<Context>::CreateUninitialized(context);
164 Node* context_node = m.NewNode(common.HeapConstant(context_constant));
166 m.CallJS0(deopt_fun_node, m.UndefinedConstant(), context_node, state_node);
168 m.Return(m.UndefinedConstant());
170 // Schedule the graph:
171 Schedule* schedule = m.Export();
176 BailoutId GetCallBailoutId() {
177 ZoneList<Statement*>* body = info.function()->body();
178 for (int i = 0; i < body->length(); i++) {
179 if (body->at(i)->IsExpressionStatement() &&
180 body->at(i)->AsExpressionStatement()->expression()->IsCall()) {
181 return body->at(i)->AsExpressionStatement()->expression()->id();
185 return BailoutId(-1);
190 TEST(TurboTrivialDeoptCodegen) {
191 HandleAndZoneScope scope;
192 InitializedHandleScope handles;
194 FLAG_allow_natives_syntax = true;
195 FLAG_turbo_deoptimization = true;
197 TrivialDeoptCodegenTester t(&scope);
200 DeoptimizationInputData* data =
201 DeoptimizationInputData::cast(t.result_code->deoptimization_data());
203 // TODO(jarin) Find a way to test the safepoint.
205 // Check that we deoptimize to the right AST id.
206 CHECK_EQ(1, data->DeoptCount());
207 CHECK_EQ(t.bailout_id.ToInt(), data->AstId(0).ToInt());
211 TEST(TurboTrivialDeoptCodegenAndRun) {
212 HandleAndZoneScope scope;
213 InitializedHandleScope handles;
215 FLAG_allow_natives_syntax = true;
216 FLAG_turbo_deoptimization = true;
218 TrivialDeoptCodegenTester t(&scope);
221 t.function->ReplaceCode(*t.result_code);
222 t.info.context()->native_context()->AddOptimizedCode(*t.result_code);
224 Isolate* isolate = scope.main_isolate();
225 Handle<Object> result;
226 bool has_pending_exception =
227 !Execution::Call(isolate, t.function,
228 isolate->factory()->undefined_value(), 0, NULL,
229 false).ToHandle(&result);
230 CHECK(!has_pending_exception);
231 CHECK(result->SameValue(Smi::FromInt(42)));
235 class TrivialRuntimeDeoptCodegenTester : public DeoptCodegenTester {
237 explicit TrivialRuntimeDeoptCodegenTester(HandleAndZoneScope* scope)
238 : DeoptCodegenTester(
240 "function foo() { %DeoptimizeFunction(foo); return 42; }; foo") {}
242 void GenerateCode() {
243 GenerateCodeFromSchedule(BuildGraphAndSchedule(graph));
246 Schedule* BuildGraphAndSchedule(Graph* graph) {
247 CommonOperatorBuilder common(zone());
249 // Manually construct a schedule for the function below:
251 // %DeoptimizeFunction(foo);
254 CSignature1<Object*, Object*> sig;
255 RawMachineAssembler m(graph, &sig);
257 Unique<HeapObject> this_fun_constant =
258 Unique<HeapObject>::CreateUninitialized(function);
259 Node* this_fun_node = m.NewNode(common.HeapConstant(this_fun_constant));
261 Handle<Context> context(function->context(), CcTest::i_isolate());
262 Unique<HeapObject> context_constant =
263 Unique<HeapObject>::CreateUninitialized(context);
264 Node* context_node = m.NewNode(common.HeapConstant(context_constant));
266 bailout_id = GetCallBailoutId();
267 Node* parameters = m.NewNode(common.StateValues(1), m.UndefinedConstant());
268 Node* locals = m.NewNode(common.StateValues(0));
269 Node* stack = m.NewNode(common.StateValues(0));
271 Node* state_node = m.NewNode(
272 common.FrameState(JS_FRAME, bailout_id,
273 OutputFrameStateCombine::Ignore()),
274 parameters, locals, stack, context_node, m.UndefinedConstant());
276 m.CallRuntime1(Runtime::kDeoptimizeFunction, this_fun_node, context_node,
279 m.Return(m.UndefinedConstant());
281 // Schedule the graph:
282 Schedule* schedule = m.Export();
287 BailoutId GetCallBailoutId() {
288 ZoneList<Statement*>* body = info.function()->body();
289 for (int i = 0; i < body->length(); i++) {
290 if (body->at(i)->IsExpressionStatement() &&
291 body->at(i)->AsExpressionStatement()->expression()->IsCallRuntime()) {
292 return body->at(i)->AsExpressionStatement()->expression()->id();
296 return BailoutId(-1);
301 TEST(TurboTrivialRuntimeDeoptCodegenAndRun) {
302 HandleAndZoneScope scope;
303 InitializedHandleScope handles;
305 FLAG_allow_natives_syntax = true;
306 FLAG_turbo_deoptimization = true;
308 TrivialRuntimeDeoptCodegenTester t(&scope);
311 t.function->ReplaceCode(*t.result_code);
312 t.info.context()->native_context()->AddOptimizedCode(*t.result_code);
314 Isolate* isolate = scope.main_isolate();
315 Handle<Object> result;
316 bool has_pending_exception =
317 !Execution::Call(isolate, t.function,
318 isolate->factory()->undefined_value(), 0, NULL,
319 false).ToHandle(&result);
320 CHECK(!has_pending_exception);
321 CHECK(result->SameValue(Smi::FromInt(42)));