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