| 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 |