1// Copyright 2012 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/code-stubs.h" 6 7#include <memory> 8 9#include "src/bailout-reason.h" 10#include "src/code-factory.h" 11#include "src/code-stub-assembler.h" 12#include "src/crankshaft/hydrogen.h" 13#include "src/crankshaft/lithium.h" 14#include "src/field-index.h" 15#include "src/ic/ic.h" 16#include "src/objects-inl.h" 17 18namespace v8 { 19namespace internal { 20 21 22static LChunk* OptimizeGraph(HGraph* graph) { 23 DisallowHeapAllocation no_allocation; 24 DisallowHandleAllocation no_handles; 25 DisallowHandleDereference no_deref; 26 27 DCHECK(graph != NULL); 28 BailoutReason bailout_reason = kNoReason; 29 if (!graph->Optimize(&bailout_reason)) { 30 FATAL(GetBailoutReason(bailout_reason)); 31 } 32 LChunk* chunk = LChunk::NewChunk(graph); 33 if (chunk == NULL) { 34 FATAL(GetBailoutReason(graph->info()->bailout_reason())); 35 } 36 return chunk; 37} 38 39 40class CodeStubGraphBuilderBase : public HGraphBuilder { 41 public: 42 explicit CodeStubGraphBuilderBase(CompilationInfo* info, CodeStub* code_stub) 43 : HGraphBuilder(info, code_stub->GetCallInterfaceDescriptor(), false), 44 arguments_length_(NULL), 45 info_(info), 46 code_stub_(code_stub), 47 descriptor_(code_stub), 48 context_(NULL) { 49 int parameter_count = GetParameterCount(); 50 parameters_.reset(new HParameter*[parameter_count]); 51 } 52 virtual bool BuildGraph(); 53 54 protected: 55 virtual HValue* BuildCodeStub() = 0; 56 int GetParameterCount() const { return descriptor_.GetParameterCount(); } 57 int GetRegisterParameterCount() const { 58 return descriptor_.GetRegisterParameterCount(); 59 } 60 HParameter* GetParameter(int parameter) { 61 DCHECK(parameter < GetParameterCount()); 62 return parameters_[parameter]; 63 } 64 Representation GetParameterRepresentation(int parameter) { 65 return RepresentationFromMachineType( 66 descriptor_.GetParameterType(parameter)); 67 } 68 bool IsParameterCountRegister(int index) const { 69 return descriptor_.GetRegisterParameter(index) 70 .is(descriptor_.stack_parameter_count()); 71 } 72 HValue* GetArgumentsLength() { 73 // This is initialized in BuildGraph() 74 DCHECK(arguments_length_ != NULL); 75 return arguments_length_; 76 } 77 CompilationInfo* info() { return info_; } 78 CodeStub* stub() { return code_stub_; } 79 HContext* context() { return context_; } 80 Isolate* isolate() { return info_->isolate(); } 81 82 private: 83 std::unique_ptr<HParameter* []> parameters_; 84 HValue* arguments_length_; 85 CompilationInfo* info_; 86 CodeStub* code_stub_; 87 CodeStubDescriptor descriptor_; 88 HContext* context_; 89}; 90 91 92bool CodeStubGraphBuilderBase::BuildGraph() { 93 // Update the static counter each time a new code stub is generated. 94 isolate()->counters()->code_stubs()->Increment(); 95 96 if (FLAG_trace_hydrogen_stubs) { 97 const char* name = CodeStub::MajorName(stub()->MajorKey()); 98 PrintF("-----------------------------------------------------------\n"); 99 PrintF("Compiling stub %s using hydrogen\n", name); 100 isolate()->GetHTracer()->TraceCompilation(info()); 101 } 102 103 int param_count = GetParameterCount(); 104 int register_param_count = GetRegisterParameterCount(); 105 HEnvironment* start_environment = graph()->start_environment(); 106 HBasicBlock* next_block = CreateBasicBlock(start_environment); 107 Goto(next_block); 108 next_block->SetJoinId(BailoutId::StubEntry()); 109 set_current_block(next_block); 110 111 bool runtime_stack_params = descriptor_.stack_parameter_count().is_valid(); 112 HInstruction* stack_parameter_count = NULL; 113 for (int i = 0; i < param_count; ++i) { 114 Representation r = GetParameterRepresentation(i); 115 HParameter* param; 116 if (i >= register_param_count) { 117 param = Add<HParameter>(i - register_param_count, 118 HParameter::STACK_PARAMETER, r); 119 } else { 120 param = Add<HParameter>(i, HParameter::REGISTER_PARAMETER, r); 121 } 122 start_environment->Bind(i, param); 123 parameters_[i] = param; 124 if (i < register_param_count && IsParameterCountRegister(i)) { 125 param->set_type(HType::Smi()); 126 stack_parameter_count = param; 127 arguments_length_ = stack_parameter_count; 128 } 129 } 130 131 DCHECK(!runtime_stack_params || arguments_length_ != NULL); 132 if (!runtime_stack_params) { 133 stack_parameter_count = 134 Add<HConstant>(param_count - register_param_count - 1); 135 // graph()->GetConstantMinus1(); 136 arguments_length_ = graph()->GetConstant0(); 137 } 138 139 context_ = Add<HContext>(); 140 start_environment->BindContext(context_); 141 start_environment->Bind(param_count, context_); 142 143 Add<HSimulate>(BailoutId::StubEntry()); 144 145 NoObservableSideEffectsScope no_effects(this); 146 147 HValue* return_value = BuildCodeStub(); 148 149 // We might have extra expressions to pop from the stack in addition to the 150 // arguments above. 151 HInstruction* stack_pop_count = stack_parameter_count; 152 if (descriptor_.function_mode() == JS_FUNCTION_STUB_MODE) { 153 if (!stack_parameter_count->IsConstant() && 154 descriptor_.hint_stack_parameter_count() < 0) { 155 HInstruction* constant_one = graph()->GetConstant1(); 156 stack_pop_count = AddUncasted<HAdd>(stack_parameter_count, constant_one); 157 stack_pop_count->ClearFlag(HValue::kCanOverflow); 158 // TODO(mvstanton): verify that stack_parameter_count+1 really fits in a 159 // smi. 160 } else { 161 int count = descriptor_.hint_stack_parameter_count(); 162 stack_pop_count = Add<HConstant>(count); 163 } 164 } 165 166 if (current_block() != NULL) { 167 HReturn* hreturn_instruction = New<HReturn>(return_value, 168 stack_pop_count); 169 FinishCurrentBlock(hreturn_instruction); 170 } 171 return true; 172} 173 174 175template <class Stub> 176class CodeStubGraphBuilder: public CodeStubGraphBuilderBase { 177 public: 178 explicit CodeStubGraphBuilder(CompilationInfo* info, CodeStub* stub) 179 : CodeStubGraphBuilderBase(info, stub) {} 180 181 typedef typename Stub::Descriptor Descriptor; 182 183 protected: 184 virtual HValue* BuildCodeStub() { 185 if (casted_stub()->IsUninitialized()) { 186 return BuildCodeUninitializedStub(); 187 } else { 188 return BuildCodeInitializedStub(); 189 } 190 } 191 192 virtual HValue* BuildCodeInitializedStub() { 193 UNIMPLEMENTED(); 194 return NULL; 195 } 196 197 virtual HValue* BuildCodeUninitializedStub() { 198 // Force a deopt that falls back to the runtime. 199 HValue* undefined = graph()->GetConstantUndefined(); 200 IfBuilder builder(this); 201 builder.IfNot<HCompareObjectEqAndBranch, HValue*>(undefined, undefined); 202 builder.Then(); 203 builder.ElseDeopt(DeoptimizeReason::kForcedDeoptToRuntime); 204 return undefined; 205 } 206 207 Stub* casted_stub() { return static_cast<Stub*>(stub()); } 208}; 209 210 211Handle<Code> HydrogenCodeStub::GenerateLightweightMissCode( 212 ExternalReference miss) { 213 Factory* factory = isolate()->factory(); 214 215 // Generate the new code. 216 MacroAssembler masm(isolate(), NULL, 256, CodeObjectRequired::kYes); 217 218 { 219 // Update the static counter each time a new code stub is generated. 220 isolate()->counters()->code_stubs()->Increment(); 221 222 // Generate the code for the stub. 223 masm.set_generating_stub(true); 224 // TODO(yangguo): remove this once we can serialize IC stubs. 225 masm.enable_serializer(); 226 NoCurrentFrameScope scope(&masm); 227 GenerateLightweightMiss(&masm, miss); 228 } 229 230 // Create the code object. 231 CodeDesc desc; 232 masm.GetCode(&desc); 233 234 // Copy the generated code into a heap object. 235 Handle<Code> new_object = factory->NewCode( 236 desc, GetCodeFlags(), masm.CodeObject(), NeedsImmovableCode()); 237 return new_object; 238} 239 240Handle<Code> HydrogenCodeStub::GenerateRuntimeTailCall( 241 CodeStubDescriptor* descriptor) { 242 const char* name = CodeStub::MajorName(MajorKey()); 243 Zone zone(isolate()->allocator(), ZONE_NAME); 244 CallInterfaceDescriptor interface_descriptor(GetCallInterfaceDescriptor()); 245 compiler::CodeAssemblerState state(isolate(), &zone, interface_descriptor, 246 GetCodeFlags(), name); 247 CodeStubAssembler assembler(&state); 248 int total_params = interface_descriptor.GetStackParameterCount() + 249 interface_descriptor.GetRegisterParameterCount(); 250 switch (total_params) { 251 case 0: 252 assembler.TailCallRuntime(descriptor->miss_handler_id(), 253 assembler.Parameter(0)); 254 break; 255 case 1: 256 assembler.TailCallRuntime(descriptor->miss_handler_id(), 257 assembler.Parameter(1), assembler.Parameter(0)); 258 break; 259 case 2: 260 assembler.TailCallRuntime(descriptor->miss_handler_id(), 261 assembler.Parameter(2), assembler.Parameter(0), 262 assembler.Parameter(1)); 263 break; 264 case 3: 265 assembler.TailCallRuntime(descriptor->miss_handler_id(), 266 assembler.Parameter(3), assembler.Parameter(0), 267 assembler.Parameter(1), assembler.Parameter(2)); 268 break; 269 case 4: 270 assembler.TailCallRuntime(descriptor->miss_handler_id(), 271 assembler.Parameter(4), assembler.Parameter(0), 272 assembler.Parameter(1), assembler.Parameter(2), 273 assembler.Parameter(3)); 274 break; 275 default: 276 UNIMPLEMENTED(); 277 break; 278 } 279 return compiler::CodeAssembler::GenerateCode(&state); 280} 281 282template <class Stub> 283static Handle<Code> DoGenerateCode(Stub* stub) { 284 Isolate* isolate = stub->isolate(); 285 CodeStubDescriptor descriptor(stub); 286 287 if (FLAG_minimal && descriptor.has_miss_handler()) { 288 return stub->GenerateRuntimeTailCall(&descriptor); 289 } 290 291 // If we are uninitialized we can use a light-weight stub to enter 292 // the runtime that is significantly faster than using the standard 293 // stub-failure deopt mechanism. 294 if (stub->IsUninitialized() && descriptor.has_miss_handler()) { 295 DCHECK(!descriptor.stack_parameter_count().is_valid()); 296 return stub->GenerateLightweightMissCode(descriptor.miss_handler()); 297 } 298 base::ElapsedTimer timer; 299 if (FLAG_profile_hydrogen_code_stub_compilation) { 300 timer.Start(); 301 } 302 Zone zone(isolate->allocator(), ZONE_NAME); 303 CompilationInfo info(CStrVector(CodeStub::MajorName(stub->MajorKey())), 304 isolate, &zone, stub->GetCodeFlags()); 305 // Parameter count is number of stack parameters. 306 int parameter_count = descriptor.GetStackParameterCount(); 307 if (descriptor.function_mode() == NOT_JS_FUNCTION_STUB_MODE) { 308 parameter_count--; 309 } 310 info.set_parameter_count(parameter_count); 311 CodeStubGraphBuilder<Stub> builder(&info, stub); 312 LChunk* chunk = OptimizeGraph(builder.CreateGraph()); 313 Handle<Code> code = chunk->Codegen(); 314 if (FLAG_profile_hydrogen_code_stub_compilation) { 315 OFStream os(stdout); 316 os << "[Lazy compilation of " << stub << " took " 317 << timer.Elapsed().InMillisecondsF() << " ms]" << std::endl; 318 } 319 return code; 320} 321 322template <> 323HValue* CodeStubGraphBuilder<TransitionElementsKindStub>::BuildCodeStub() { 324 ElementsKind const from_kind = casted_stub()->from_kind(); 325 ElementsKind const to_kind = casted_stub()->to_kind(); 326 HValue* const object = GetParameter(Descriptor::kObject); 327 HValue* const map = GetParameter(Descriptor::kMap); 328 329 // The {object} is known to be a JSObject (otherwise it wouldn't have elements 330 // anyways). 331 object->set_type(HType::JSObject()); 332 333 info()->MarkAsSavesCallerDoubles(); 334 335 DCHECK_IMPLIES(IsFastHoleyElementsKind(from_kind), 336 IsFastHoleyElementsKind(to_kind)); 337 338 if (AllocationSite::GetMode(from_kind, to_kind) == TRACK_ALLOCATION_SITE) { 339 Add<HTrapAllocationMemento>(object); 340 } 341 342 if (!IsSimpleMapChangeTransition(from_kind, to_kind)) { 343 HInstruction* elements = AddLoadElements(object); 344 345 IfBuilder if_objecthaselements(this); 346 if_objecthaselements.IfNot<HCompareObjectEqAndBranch>( 347 elements, Add<HConstant>(isolate()->factory()->empty_fixed_array())); 348 if_objecthaselements.Then(); 349 { 350 // Determine the elements capacity. 351 HInstruction* elements_length = AddLoadFixedArrayLength(elements); 352 353 // Determine the effective (array) length. 354 IfBuilder if_objectisarray(this); 355 if_objectisarray.If<HHasInstanceTypeAndBranch>(object, JS_ARRAY_TYPE); 356 if_objectisarray.Then(); 357 { 358 // The {object} is a JSArray, load the special "length" property. 359 Push(Add<HLoadNamedField>(object, nullptr, 360 HObjectAccess::ForArrayLength(from_kind))); 361 } 362 if_objectisarray.Else(); 363 { 364 // The {object} is some other JSObject. 365 Push(elements_length); 366 } 367 if_objectisarray.End(); 368 HValue* length = Pop(); 369 370 BuildGrowElementsCapacity(object, elements, from_kind, to_kind, length, 371 elements_length); 372 } 373 if_objecthaselements.End(); 374 } 375 376 Add<HStoreNamedField>(object, HObjectAccess::ForMap(), map); 377 378 return object; 379} 380 381 382Handle<Code> TransitionElementsKindStub::GenerateCode() { 383 return DoGenerateCode(this); 384} 385 386template <> 387HValue* CodeStubGraphBuilder<BinaryOpICStub>::BuildCodeInitializedStub() { 388 BinaryOpICState state = casted_stub()->state(); 389 390 HValue* left = GetParameter(Descriptor::kLeft); 391 HValue* right = GetParameter(Descriptor::kRight); 392 393 AstType* left_type = state.GetLeftType(); 394 AstType* right_type = state.GetRightType(); 395 AstType* result_type = state.GetResultType(); 396 397 DCHECK(!left_type->Is(AstType::None()) && !right_type->Is(AstType::None()) && 398 (state.HasSideEffects() || !result_type->Is(AstType::None()))); 399 400 HValue* result = NULL; 401 HAllocationMode allocation_mode(NOT_TENURED); 402 if (state.op() == Token::ADD && (left_type->Maybe(AstType::String()) || 403 right_type->Maybe(AstType::String())) && 404 !left_type->Is(AstType::String()) && !right_type->Is(AstType::String())) { 405 // For the generic add stub a fast case for string addition is performance 406 // critical. 407 if (left_type->Maybe(AstType::String())) { 408 IfBuilder if_leftisstring(this); 409 if_leftisstring.If<HIsStringAndBranch>(left); 410 if_leftisstring.Then(); 411 { 412 Push(BuildBinaryOperation(state.op(), left, right, AstType::String(), 413 right_type, result_type, 414 state.fixed_right_arg(), allocation_mode)); 415 } 416 if_leftisstring.Else(); 417 { 418 Push(BuildBinaryOperation(state.op(), left, right, left_type, 419 right_type, result_type, 420 state.fixed_right_arg(), allocation_mode)); 421 } 422 if_leftisstring.End(); 423 result = Pop(); 424 } else { 425 IfBuilder if_rightisstring(this); 426 if_rightisstring.If<HIsStringAndBranch>(right); 427 if_rightisstring.Then(); 428 { 429 Push(BuildBinaryOperation(state.op(), left, right, left_type, 430 AstType::String(), result_type, 431 state.fixed_right_arg(), allocation_mode)); 432 } 433 if_rightisstring.Else(); 434 { 435 Push(BuildBinaryOperation(state.op(), left, right, left_type, 436 right_type, result_type, 437 state.fixed_right_arg(), allocation_mode)); 438 } 439 if_rightisstring.End(); 440 result = Pop(); 441 } 442 } else { 443 result = BuildBinaryOperation(state.op(), left, right, left_type, 444 right_type, result_type, 445 state.fixed_right_arg(), allocation_mode); 446 } 447 448 // If we encounter a generic argument, the number conversion is 449 // observable, thus we cannot afford to bail out after the fact. 450 if (!state.HasSideEffects()) { 451 result = EnforceNumberType(result, result_type); 452 } 453 454 return result; 455} 456 457 458Handle<Code> BinaryOpICStub::GenerateCode() { 459 return DoGenerateCode(this); 460} 461 462 463template <> 464HValue* CodeStubGraphBuilder<BinaryOpWithAllocationSiteStub>::BuildCodeStub() { 465 BinaryOpICState state = casted_stub()->state(); 466 467 HValue* allocation_site = GetParameter(Descriptor::kAllocationSite); 468 HValue* left = GetParameter(Descriptor::kLeft); 469 HValue* right = GetParameter(Descriptor::kRight); 470 471 AstType* left_type = state.GetLeftType(); 472 AstType* right_type = state.GetRightType(); 473 AstType* result_type = state.GetResultType(); 474 HAllocationMode allocation_mode(allocation_site); 475 476 return BuildBinaryOperation(state.op(), left, right, left_type, right_type, 477 result_type, state.fixed_right_arg(), 478 allocation_mode); 479} 480 481 482Handle<Code> BinaryOpWithAllocationSiteStub::GenerateCode() { 483 return DoGenerateCode(this); 484} 485 486 487template <> 488HValue* CodeStubGraphBuilder<ToBooleanICStub>::BuildCodeInitializedStub() { 489 ToBooleanICStub* stub = casted_stub(); 490 IfBuilder if_true(this); 491 if_true.If<HBranch>(GetParameter(Descriptor::kArgument), stub->hints()); 492 if_true.Then(); 493 if_true.Return(graph()->GetConstantTrue()); 494 if_true.Else(); 495 if_true.End(); 496 return graph()->GetConstantFalse(); 497} 498 499Handle<Code> ToBooleanICStub::GenerateCode() { return DoGenerateCode(this); } 500 501} // namespace internal 502} // namespace v8 503