1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided
11 // with the distribution.
12 // * Neither the name of Google Inc. nor the names of its
13 // contributors may be used to endorse or promote products derived
14 // from this software without specific prior written permission.
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include "code-stubs.h"
38 static LChunk* OptimizeGraph(HGraph* graph) {
39 Isolate* isolate = graph->isolate();
40 AssertNoAllocation no_gc;
41 NoHandleAllocation no_handles(isolate);
42 HandleDereferenceGuard no_deref(isolate, HandleDereferenceGuard::DISALLOW);
44 ASSERT(graph != NULL);
45 SmartArrayPointer<char> bailout_reason;
46 if (!graph->Optimize(&bailout_reason)) {
47 FATAL(bailout_reason.is_empty() ? "unknown" : *bailout_reason);
49 LChunk* chunk = LChunk::NewChunk(graph);
51 FATAL(graph->info()->bailout_reason());
57 class CodeStubGraphBuilderBase : public HGraphBuilder {
59 CodeStubGraphBuilderBase(Isolate* isolate, HydrogenCodeStub* stub)
60 : HGraphBuilder(&info_),
61 arguments_length_(NULL),
64 descriptor_ = stub->GetInterfaceDescriptor(isolate);
65 parameters_.Reset(new HParameter*[descriptor_->register_param_count_]);
67 virtual bool BuildGraph();
70 virtual HValue* BuildCodeStub() = 0;
71 HParameter* GetParameter(int parameter) {
72 ASSERT(parameter < descriptor_->register_param_count_);
73 return parameters_[parameter];
75 HValue* GetArgumentsLength() {
76 // This is initialized in BuildGraph()
77 ASSERT(arguments_length_ != NULL);
78 return arguments_length_;
80 CompilationInfo* info() { return &info_; }
81 HydrogenCodeStub* stub() { return info_.code_stub(); }
82 HContext* context() { return context_; }
83 Isolate* isolate() { return info_.isolate(); }
85 class ArrayContextChecker {
87 ArrayContextChecker(HGraphBuilder* builder, HValue* constructor,
88 HValue* array_function)
90 checker_.If<HCompareObjectEqAndBranch, HValue*>(constructor,
95 ~ArrayContextChecker() {
104 SmartArrayPointer<HParameter*> parameters_;
105 HValue* arguments_length_;
106 CompilationInfoWithZone info_;
107 CodeStubInterfaceDescriptor* descriptor_;
112 bool CodeStubGraphBuilderBase::BuildGraph() {
113 // Update the static counter each time a new code stub is generated.
114 isolate()->counters()->code_stubs()->Increment();
116 if (FLAG_trace_hydrogen) {
117 const char* name = CodeStub::MajorName(stub()->MajorKey(), false);
118 PrintF("-----------------------------------------------------------\n");
119 PrintF("Compiling stub %s using hydrogen\n", name);
120 isolate()->GetHTracer()->TraceCompilation(&info_);
123 Zone* zone = this->zone();
124 int param_count = descriptor_->register_param_count_;
125 HEnvironment* start_environment = graph()->start_environment();
126 HBasicBlock* next_block = CreateBasicBlock(start_environment);
127 current_block()->Goto(next_block);
128 next_block->SetJoinId(BailoutId::StubEntry());
129 set_current_block(next_block);
131 HConstant* undefined_constant = new(zone) HConstant(
132 isolate()->factory()->undefined_value(), Representation::Tagged());
133 AddInstruction(undefined_constant);
134 graph()->set_undefined_constant(undefined_constant);
136 for (int i = 0; i < param_count; ++i) {
138 new(zone) HParameter(i, HParameter::REGISTER_PARAMETER);
139 AddInstruction(param);
140 start_environment->Bind(i, param);
141 parameters_[i] = param;
144 HInstruction* stack_parameter_count;
145 if (descriptor_->stack_parameter_count_ != NULL) {
146 ASSERT(descriptor_->environment_length() == (param_count + 1));
147 stack_parameter_count = new(zone) HParameter(param_count,
148 HParameter::REGISTER_PARAMETER,
149 Representation::Integer32());
150 stack_parameter_count->set_type(HType::Smi());
151 // it's essential to bind this value to the environment in case of deopt
152 AddInstruction(stack_parameter_count);
153 start_environment->Bind(param_count, stack_parameter_count);
154 arguments_length_ = stack_parameter_count;
156 ASSERT(descriptor_->environment_length() == param_count);
157 stack_parameter_count = graph()->GetConstantMinus1();
158 arguments_length_ = graph()->GetConstant0();
161 context_ = new(zone) HContext();
162 AddInstruction(context_);
163 start_environment->BindContext(context_);
165 AddSimulate(BailoutId::StubEntry());
167 NoObservableSideEffectsScope no_effects(this);
169 HValue* return_value = BuildCodeStub();
171 // We might have extra expressions to pop from the stack in addition to the
173 HInstruction* stack_pop_count = stack_parameter_count;
174 if (descriptor_->function_mode_ == JS_FUNCTION_STUB_MODE) {
175 if (!stack_parameter_count->IsConstant() &&
176 descriptor_->hint_stack_parameter_count_ < 0) {
177 HInstruction* amount = graph()->GetConstant1();
178 stack_pop_count = AddInstruction(
179 HAdd::New(zone, context_, stack_parameter_count, amount));
180 stack_pop_count->ChangeRepresentation(Representation::Integer32());
181 stack_pop_count->ClearFlag(HValue::kCanOverflow);
183 int count = descriptor_->hint_stack_parameter_count_;
184 stack_pop_count = AddInstruction(new(zone)
185 HConstant(count, Representation::Integer32()));
189 if (!current_block()->IsFinished()) {
190 HReturn* hreturn_instruction = new(zone) HReturn(return_value,
193 current_block()->Finish(hreturn_instruction);
199 template <class Stub>
200 class CodeStubGraphBuilder: public CodeStubGraphBuilderBase {
202 explicit CodeStubGraphBuilder(Stub* stub)
203 : CodeStubGraphBuilderBase(Isolate::Current(), stub) {}
206 virtual HValue* BuildCodeStub() {
207 if (casted_stub()->IsMiss()) {
208 return BuildCodeInitializedStub();
210 return BuildCodeUninitializedStub();
214 virtual HValue* BuildCodeInitializedStub() {
219 virtual HValue* BuildCodeUninitializedStub() {
220 // Force a deopt that falls back to the runtime.
221 HValue* undefined = graph()->GetConstantUndefined();
222 IfBuilder builder(this);
223 builder.IfNot<HCompareObjectEqAndBranch, HValue*>(undefined, undefined);
229 Stub* casted_stub() { return static_cast<Stub*>(stub()); }
233 Handle<Code> HydrogenCodeStub::GenerateLightweightMissCode(Isolate* isolate) {
234 Factory* factory = isolate->factory();
236 // Generate the new code.
237 MacroAssembler masm(isolate, NULL, 256);
240 // Update the static counter each time a new code stub is generated.
241 isolate->counters()->code_stubs()->Increment();
243 // Nested stubs are not allowed for leaves.
244 AllowStubCallsScope allow_scope(&masm, false);
246 // Generate the code for the stub.
247 masm.set_generating_stub(true);
248 NoCurrentFrameScope scope(&masm);
249 GenerateLightweightMiss(&masm);
252 // Create the code object.
256 // Copy the generated code into a heap object.
257 Code::Flags flags = Code::ComputeFlags(
263 Handle<Code> new_object = factory->NewCode(
264 desc, flags, masm.CodeObject(), NeedsImmovableCode());
269 template <class Stub>
270 static Handle<Code> DoGenerateCode(Stub* stub) {
271 Isolate* isolate = Isolate::Current();
272 CodeStub::Major major_key =
273 static_cast<HydrogenCodeStub*>(stub)->MajorKey();
274 CodeStubInterfaceDescriptor* descriptor =
275 isolate->code_stub_interface_descriptor(major_key);
276 if (descriptor->register_param_count_ < 0) {
277 stub->InitializeInterfaceDescriptor(isolate, descriptor);
279 // The miss case without stack parameters can use a light-weight stub to enter
280 // the runtime that is significantly faster than using the standard
281 // stub-failure deopt mechanism.
282 if (stub->IsMiss() && descriptor->stack_parameter_count_ == NULL) {
283 return stub->GenerateLightweightMissCode(isolate);
285 CodeStubGraphBuilder<Stub> builder(stub);
286 LChunk* chunk = OptimizeGraph(builder.CreateGraph());
287 return chunk->Codegen();
293 HValue* CodeStubGraphBuilder<FastCloneShallowArrayStub>::BuildCodeStub() {
294 Zone* zone = this->zone();
295 Factory* factory = isolate()->factory();
296 HValue* undefined = graph()->GetConstantUndefined();
297 AllocationSiteMode alloc_site_mode = casted_stub()->allocation_site_mode();
298 FastCloneShallowArrayStub::Mode mode = casted_stub()->mode();
299 int length = casted_stub()->length();
301 HInstruction* boilerplate =
302 AddInstruction(new(zone) HLoadKeyed(GetParameter(0),
307 IfBuilder checker(this);
308 checker.IfNot<HCompareObjectEqAndBranch, HValue*>(boilerplate, undefined);
311 if (mode == FastCloneShallowArrayStub::CLONE_ANY_ELEMENTS) {
312 HValue* elements = AddLoadElements(boilerplate);
314 IfBuilder if_fixed_cow(this);
315 if_fixed_cow.IfCompareMap(elements, factory->fixed_cow_array_map());
317 environment()->Push(BuildCloneShallowArray(context(),
321 0/*copy-on-write*/));
324 IfBuilder if_fixed(this);
325 if_fixed.IfCompareMap(elements, factory->fixed_array_map());
327 environment()->Push(BuildCloneShallowArray(context(),
333 environment()->Push(BuildCloneShallowArray(context(),
336 FAST_DOUBLE_ELEMENTS,
339 ElementsKind elements_kind = casted_stub()->ComputeElementsKind();
340 environment()->Push(BuildCloneShallowArray(context(),
347 HValue* result = environment()->Pop();
353 Handle<Code> FastCloneShallowArrayStub::GenerateCode() {
354 return DoGenerateCode(this);
359 HValue* CodeStubGraphBuilder<FastCloneShallowObjectStub>::BuildCodeStub() {
360 Zone* zone = this->zone();
361 Factory* factory = isolate()->factory();
362 HValue* undefined = graph()->GetConstantUndefined();
364 HInstruction* boilerplate =
365 AddInstruction(new(zone) HLoadKeyed(GetParameter(0),
370 IfBuilder checker(this);
371 checker.IfNot<HCompareObjectEqAndBranch, HValue*>(boilerplate, undefined);
374 int size = JSObject::kHeaderSize + casted_stub()->length() * kPointerSize;
375 HValue* boilerplate_size =
376 AddInstruction(new(zone) HInstanceSize(boilerplate));
377 HValue* size_in_words =
378 AddInstruction(new(zone) HConstant(size >> kPointerSizeLog2,
379 Representation::Integer32()));
380 checker.IfCompare(boilerplate_size, size_in_words, Token::EQ);
383 HValue* size_in_bytes =
384 AddInstruction(new(zone) HConstant(size, Representation::Integer32()));
385 HAllocate::Flags flags = HAllocate::CAN_ALLOCATE_IN_NEW_SPACE;
386 if (FLAG_pretenure_literals) {
387 flags = static_cast<HAllocate::Flags>(
388 flags | HAllocate::CAN_ALLOCATE_IN_OLD_POINTER_SPACE);
390 HInstruction* object =
391 AddInstruction(new(zone) HAllocate(context(),
396 for (int i = 0; i < size; i += kPointerSize) {
397 HInstruction* value =
398 AddInstruction(new(zone) HLoadNamedField(
399 boilerplate, true, Representation::Tagged(), i));
400 AddInstruction(new(zone) HStoreNamedField(object,
401 factory->empty_string(),
403 Representation::Tagged(), i));
411 Handle<Code> FastCloneShallowObjectStub::GenerateCode() {
412 return DoGenerateCode(this);
417 HValue* CodeStubGraphBuilder<KeyedLoadFastElementStub>::BuildCodeStub() {
418 HInstruction* load = BuildUncheckedMonomorphicElementAccess(
419 GetParameter(0), GetParameter(1), NULL, NULL,
420 casted_stub()->is_js_array(), casted_stub()->elements_kind(),
421 false, NEVER_RETURN_HOLE, STANDARD_STORE, Representation::Tagged());
426 Handle<Code> KeyedLoadFastElementStub::GenerateCode() {
427 return DoGenerateCode(this);
432 HValue* CodeStubGraphBuilder<LoadFieldStub>::BuildCodeStub() {
433 Representation representation = casted_stub()->representation();
434 HInstruction* load = AddInstruction(DoBuildLoadNamedField(
435 GetParameter(0), casted_stub()->is_inobject(),
436 representation, casted_stub()->offset()));
441 Handle<Code> LoadFieldStub::GenerateCode() {
442 return DoGenerateCode(this);
447 HValue* CodeStubGraphBuilder<KeyedLoadFieldStub>::BuildCodeStub() {
448 Representation representation = casted_stub()->representation();
449 HInstruction* load = AddInstruction(DoBuildLoadNamedField(
450 GetParameter(0), casted_stub()->is_inobject(),
451 representation, casted_stub()->offset()));
456 Handle<Code> KeyedLoadFieldStub::GenerateCode() {
457 return DoGenerateCode(this);
462 HValue* CodeStubGraphBuilder<KeyedStoreFastElementStub>::BuildCodeStub() {
463 BuildUncheckedMonomorphicElementAccess(
464 GetParameter(0), GetParameter(1), GetParameter(2), NULL,
465 casted_stub()->is_js_array(), casted_stub()->elements_kind(),
466 true, NEVER_RETURN_HOLE, casted_stub()->store_mode(),
467 Representation::Tagged());
469 return GetParameter(2);
473 Handle<Code> KeyedStoreFastElementStub::GenerateCode() {
474 return DoGenerateCode(this);
479 HValue* CodeStubGraphBuilder<TransitionElementsKindStub>::BuildCodeStub() {
480 Zone* zone = this->zone();
482 HValue* js_array = GetParameter(0);
483 HValue* map = GetParameter(1);
485 info()->MarkAsSavesCallerDoubles();
487 AddInstruction(new(zone) HTrapAllocationMemento(js_array));
489 HInstruction* array_length =
490 AddInstruction(HLoadNamedField::NewArrayLength(
491 zone, js_array, js_array, HType::Smi()));
493 ElementsKind to_kind = casted_stub()->to_kind();
494 BuildNewSpaceArrayCheck(array_length, to_kind);
496 IfBuilder if_builder(this);
498 if_builder.IfCompare(array_length, graph()->GetConstant0(), Token::EQ);
501 // Nothing to do, just change the map.
505 HInstruction* elements = AddLoadElements(js_array);
507 HInstruction* elements_length =
508 AddInstruction(new(zone) HFixedArrayBaseLength(elements));
510 HValue* new_elements =
511 BuildAllocateAndInitializeElements(context(), to_kind, elements_length);
513 BuildCopyElements(context(), elements,
514 casted_stub()->from_kind(), new_elements,
515 to_kind, array_length, elements_length);
517 Factory* factory = isolate()->factory();
519 AddInstruction(new(zone) HStoreNamedField(js_array,
520 factory->elements_field_string(),
522 Representation::Tagged(),
523 JSArray::kElementsOffset));
527 AddInstruction(new(zone) HStoreNamedField(js_array, factory->length_string(),
529 Representation::Tagged(),
530 JSArray::kMapOffset));
535 Handle<Code> TransitionElementsKindStub::GenerateCode() {
536 return DoGenerateCode(this);
541 HValue* CodeStubGraphBuilder<ArrayNoArgumentConstructorStub>::BuildCodeStub() {
542 // ----------- S t a t e -------------
543 // -- Parameter 1 : type info cell
544 // -- Parameter 0 : constructor
545 // -----------------------------------
546 HInstruction* array_function = BuildGetArrayFunction(context());
547 ArrayContextChecker(this,
548 GetParameter(ArrayConstructorStubBase::kConstructor),
551 // Should be a constant
552 JSArrayBuilder array_builder(
554 casted_stub()->elements_kind(),
555 GetParameter(ArrayConstructorStubBase::kPropertyCell),
556 casted_stub()->mode());
557 return array_builder.AllocateEmptyArray();
561 Handle<Code> ArrayNoArgumentConstructorStub::GenerateCode() {
562 return DoGenerateCode(this);
567 HValue* CodeStubGraphBuilder<ArraySingleArgumentConstructorStub>::
569 HInstruction* array_function = BuildGetArrayFunction(context());
570 ArrayContextChecker(this,
571 GetParameter(ArrayConstructorStubBase::kConstructor),
573 // Smi check and range check on the input arg.
574 HValue* constant_one = graph()->GetConstant1();
575 HValue* constant_zero = graph()->GetConstant0();
577 HInstruction* elements = AddInstruction(
578 new(zone()) HArgumentsElements(false));
579 HInstruction* argument = AddInstruction(
580 new(zone()) HAccessArgumentsAt(elements, constant_one, constant_zero));
582 HConstant* max_alloc_length =
583 new(zone()) HConstant(JSObject::kInitialMaxFastElementArray,
584 Representation::Tagged());
585 AddInstruction(max_alloc_length);
586 const int initial_capacity = JSArray::kPreallocatedArrayElements;
587 HConstant* initial_capacity_node =
588 new(zone()) HConstant(initial_capacity, Representation::Tagged());
589 AddInstruction(initial_capacity_node);
591 // Since we're forcing Integer32 representation for this HBoundsCheck,
592 // there's no need to Smi-check the index.
593 HBoundsCheck* checked_arg = AddBoundsCheck(argument, max_alloc_length,
595 Representation::Tagged());
596 IfBuilder if_builder(this);
597 if_builder.IfCompare(checked_arg, constant_zero, Token::EQ);
599 Push(initial_capacity_node); // capacity
600 Push(constant_zero); // length
602 Push(checked_arg); // capacity
603 Push(checked_arg); // length
606 // Figure out total size
607 HValue* length = Pop();
608 HValue* capacity = Pop();
610 JSArrayBuilder array_builder(
612 casted_stub()->elements_kind(),
613 GetParameter(ArrayConstructorStubBase::kPropertyCell),
614 casted_stub()->mode());
615 return array_builder.AllocateArray(capacity, length, true);
619 Handle<Code> ArraySingleArgumentConstructorStub::GenerateCode() {
620 return DoGenerateCode(this);
625 HValue* CodeStubGraphBuilder<ArrayNArgumentsConstructorStub>::BuildCodeStub() {
626 HInstruction* array_function = BuildGetArrayFunction(context());
627 ArrayContextChecker(this,
628 GetParameter(ArrayConstructorStubBase::kConstructor),
630 ElementsKind kind = casted_stub()->elements_kind();
631 HValue* length = GetArgumentsLength();
633 JSArrayBuilder array_builder(
636 GetParameter(ArrayConstructorStubBase::kPropertyCell),
637 casted_stub()->mode());
639 // We need to fill with the hole if it's a smi array in the multi-argument
640 // case because we might have to bail out while copying arguments into
641 // the array because they aren't compatible with a smi array.
642 // If it's a double array, no problem, and if it's fast then no
643 // problem either because doubles are boxed.
644 bool fill_with_hole = IsFastSmiElementsKind(kind);
645 HValue* new_object = array_builder.AllocateArray(length,
648 HValue* elements = array_builder.GetElementsLocation();
649 ASSERT(elements != NULL);
651 // Now populate the elements correctly.
652 LoopBuilder builder(this,
654 LoopBuilder::kPostIncrement);
655 HValue* start = graph()->GetConstant0();
656 HValue* key = builder.BeginBody(start, length, Token::LT);
657 HInstruction* argument_elements = AddInstruction(
658 new(zone()) HArgumentsElements(false));
659 HInstruction* argument = AddInstruction(new(zone()) HAccessArgumentsAt(
660 argument_elements, length, key));
662 // Checks to prevent incompatible stores
663 if (IsFastSmiElementsKind(kind)) {
664 AddInstruction(new(zone()) HCheckSmi(argument));
667 AddInstruction(new(zone()) HStoreKeyed(elements, key, argument, kind));
673 Handle<Code> ArrayNArgumentsConstructorStub::GenerateCode() {
674 return DoGenerateCode(this);
679 HValue* CodeStubGraphBuilder<CompareNilICStub>::BuildCodeUninitializedStub() {
680 CompareNilICStub* stub = casted_stub();
681 HIfContinuation continuation;
682 Handle<Map> sentinel_map(graph()->isolate()->heap()->meta_map());
683 BuildCompareNil(GetParameter(0), stub->GetKind(),
684 stub->GetTypes(), sentinel_map,
685 RelocInfo::kNoPosition, &continuation);
686 IfBuilder if_nil(this, &continuation);
688 if (continuation.IsFalseReachable()) {
690 if_nil.Return(graph()->GetConstantSmi0());
693 return continuation.IsTrueReachable()
694 ? graph()->GetConstantSmi1()
695 : graph()->GetConstantUndefined();
699 Handle<Code> CompareNilICStub::GenerateCode() {
700 return DoGenerateCode(this);
703 } } // namespace v8::internal