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