• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
18 namespace v8 {
19 namespace internal {
20 
21 
OptimizeGraph(HGraph * graph)22 static 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 
40 class CodeStubGraphBuilderBase : public HGraphBuilder {
41  public:
CodeStubGraphBuilderBase(CompilationInfo * info,CodeStub * code_stub)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;
GetParameterCount() const56   int GetParameterCount() const { return descriptor_.GetParameterCount(); }
GetRegisterParameterCount() const57   int GetRegisterParameterCount() const {
58     return descriptor_.GetRegisterParameterCount();
59   }
GetParameter(int parameter)60   HParameter* GetParameter(int parameter) {
61     DCHECK(parameter < GetParameterCount());
62     return parameters_[parameter];
63   }
GetParameterRepresentation(int parameter)64   Representation GetParameterRepresentation(int parameter) {
65     return RepresentationFromMachineType(
66         descriptor_.GetParameterType(parameter));
67   }
IsParameterCountRegister(int index) const68   bool IsParameterCountRegister(int index) const {
69     return descriptor_.GetRegisterParameter(index)
70         .is(descriptor_.stack_parameter_count());
71   }
GetArgumentsLength()72   HValue* GetArgumentsLength() {
73     // This is initialized in BuildGraph()
74     DCHECK(arguments_length_ != NULL);
75     return arguments_length_;
76   }
info()77   CompilationInfo* info() { return info_; }
stub()78   CodeStub* stub() { return code_stub_; }
context()79   HContext* context() { return context_; }
isolate()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 
BuildGraph()92 bool 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 
175 template <class Stub>
176 class CodeStubGraphBuilder: public CodeStubGraphBuilderBase {
177  public:
CodeStubGraphBuilder(CompilationInfo * info,CodeStub * stub)178   explicit CodeStubGraphBuilder(CompilationInfo* info, CodeStub* stub)
179       : CodeStubGraphBuilderBase(info, stub) {}
180 
181   typedef typename Stub::Descriptor Descriptor;
182 
183  protected:
BuildCodeStub()184   virtual HValue* BuildCodeStub() {
185     if (casted_stub()->IsUninitialized()) {
186       return BuildCodeUninitializedStub();
187     } else {
188       return BuildCodeInitializedStub();
189     }
190   }
191 
BuildCodeInitializedStub()192   virtual HValue* BuildCodeInitializedStub() {
193     UNIMPLEMENTED();
194     return NULL;
195   }
196 
BuildCodeUninitializedStub()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 
casted_stub()207   Stub* casted_stub() { return static_cast<Stub*>(stub()); }
208 };
209 
210 
GenerateLightweightMissCode(ExternalReference miss)211 Handle<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 
GenerateRuntimeTailCall(CodeStubDescriptor * descriptor)240 Handle<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 
282 template <class Stub>
DoGenerateCode(Stub * stub)283 static 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 
322 template <>
BuildCodeStub()323 HValue* 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 
GenerateCode()382 Handle<Code> TransitionElementsKindStub::GenerateCode() {
383   return DoGenerateCode(this);
384 }
385 
386 template <>
BuildCodeInitializedStub()387 HValue* 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 
GenerateCode()458 Handle<Code> BinaryOpICStub::GenerateCode() {
459   return DoGenerateCode(this);
460 }
461 
462 
463 template <>
BuildCodeStub()464 HValue* 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 
GenerateCode()482 Handle<Code> BinaryOpWithAllocationSiteStub::GenerateCode() {
483   return DoGenerateCode(this);
484 }
485 
486 
487 template <>
BuildCodeInitializedStub()488 HValue* 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 
GenerateCode()499 Handle<Code> ToBooleanICStub::GenerateCode() { return DoGenerateCode(this); }
500 
501 }  // namespace internal
502 }  // namespace v8
503