code-stubs-hydrogen.cc 16.9 KB
Newer Older
1
// Copyright 2012 the V8 project authors. All rights reserved.
2 3
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
4

5
#include "src/code-stubs.h"
6

7 8
#include <memory>

9
#include "src/assembler-inl.h"
10
#include "src/bailout-reason.h"
11
#include "src/code-factory.h"
12
#include "src/code-stub-assembler.h"
13 14
#include "src/crankshaft/hydrogen.h"
#include "src/crankshaft/lithium.h"
15
#include "src/field-index.h"
16
#include "src/ic/ic.h"
17
#include "src/objects-inl.h"
18 19 20 21 22

namespace v8 {
namespace internal {


23
static LChunk* OptimizeGraph(HGraph* graph) {
24 25 26
  DisallowHeapAllocation no_allocation;
  DisallowHandleAllocation no_handles;
  DisallowHandleDereference no_deref;
27

28
  DCHECK(graph != NULL);
29
  BailoutReason bailout_reason = kNoReason;
30
  if (!graph->Optimize(&bailout_reason)) {
31
    FATAL(GetBailoutReason(bailout_reason));
32
  }
33
  LChunk* chunk = LChunk::NewChunk(graph);
34
  if (chunk == NULL) {
35
    FATAL(GetBailoutReason(graph->info()->bailout_reason()));
36 37
  }
  return chunk;
38 39 40 41 42
}


class CodeStubGraphBuilderBase : public HGraphBuilder {
 public:
43
  explicit CodeStubGraphBuilderBase(CompilationInfo* info, CodeStub* code_stub)
44
      : HGraphBuilder(info, code_stub->GetCallInterfaceDescriptor(), false),
45
        arguments_length_(NULL),
46
        info_(info),
47 48
        code_stub_(code_stub),
        descriptor_(code_stub),
49
        context_(NULL) {
50
    int parameter_count = GetParameterCount();
51
    parameters_.reset(new HParameter*[parameter_count]);
52
  }
53 54 55
  virtual bool BuildGraph();

 protected:
56
  virtual HValue* BuildCodeStub() = 0;
57 58
  int GetParameterCount() const { return descriptor_.GetParameterCount(); }
  int GetRegisterParameterCount() const {
59 60
    return descriptor_.GetRegisterParameterCount();
  }
61
  HParameter* GetParameter(int parameter) {
62
    DCHECK(parameter < GetParameterCount());
63 64
    return parameters_[parameter];
  }
65
  Representation GetParameterRepresentation(int parameter) {
66 67
    return RepresentationFromMachineType(
        descriptor_.GetParameterType(parameter));
68 69 70 71 72
  }
  bool IsParameterCountRegister(int index) const {
    return descriptor_.GetRegisterParameter(index)
        .is(descriptor_.stack_parameter_count());
  }
73 74
  HValue* GetArgumentsLength() {
    // This is initialized in BuildGraph()
75
    DCHECK(arguments_length_ != NULL);
76 77
    return arguments_length_;
  }
78
  CompilationInfo* info() { return info_; }
79
  CodeStub* stub() { return code_stub_; }
80
  HContext* context() { return context_; }
81
  Isolate* isolate() { return info_->isolate(); }
82 83

 private:
84
  std::unique_ptr<HParameter* []> parameters_;
85
  HValue* arguments_length_;
86
  CompilationInfo* info_;
87
  CodeStub* code_stub_;
88
  CodeStubDescriptor descriptor_;
89
  HContext* context_;
90 91 92 93
};


bool CodeStubGraphBuilderBase::BuildGraph() {
94 95 96
  // Update the static counter each time a new code stub is generated.
  isolate()->counters()->code_stubs()->Increment();

97
  if (FLAG_trace_hydrogen_stubs) {
98
    const char* name = CodeStub::MajorName(stub()->MajorKey());
99
    PrintF("-----------------------------------------------------------\n");
100
    PrintF("Compiling stub %s using hydrogen\n", name);
101
    isolate()->GetHTracer()->TraceCompilation(info());
102 103
  }

104
  int param_count = GetParameterCount();
105
  int register_param_count = GetRegisterParameterCount();
106
  HEnvironment* start_environment = graph()->start_environment();
107
  HBasicBlock* next_block = CreateBasicBlock(start_environment);
108
  Goto(next_block);
109 110 111
  next_block->SetJoinId(BailoutId::StubEntry());
  set_current_block(next_block);

112
  bool runtime_stack_params = descriptor_.stack_parameter_count().is_valid();
113
  HInstruction* stack_parameter_count = NULL;
114
  for (int i = 0; i < param_count; ++i) {
115
    Representation r = GetParameterRepresentation(i);
116 117 118 119 120 121 122
    HParameter* param;
    if (i >= register_param_count) {
      param = Add<HParameter>(i - register_param_count,
                              HParameter::STACK_PARAMETER, r);
    } else {
      param = Add<HParameter>(i, HParameter::REGISTER_PARAMETER, r);
    }
123
    start_environment->Bind(i, param);
124
    parameters_[i] = param;
125
    if (i < register_param_count && IsParameterCountRegister(i)) {
126 127 128 129
      param->set_type(HType::Smi());
      stack_parameter_count = param;
      arguments_length_ = stack_parameter_count;
    }
130
  }
131

132
  DCHECK(!runtime_stack_params || arguments_length_ != NULL);
133
  if (!runtime_stack_params) {
134 135 136
    stack_parameter_count =
        Add<HConstant>(param_count - register_param_count - 1);
    // graph()->GetConstantMinus1();
137
    arguments_length_ = graph()->GetConstant0();
138 139
  }

140
  context_ = Add<HContext>();
141
  start_environment->BindContext(context_);
142
  start_environment->Bind(param_count, context_);
143

144
  Add<HSimulate>(BailoutId::StubEntry());
145

146 147
  NoObservableSideEffectsScope no_effects(this);

148
  HValue* return_value = BuildCodeStub();
149 150

  // We might have extra expressions to pop from the stack in addition to the
151
  // arguments above.
152
  HInstruction* stack_pop_count = stack_parameter_count;
153
  if (descriptor_.function_mode() == JS_FUNCTION_STUB_MODE) {
154
    if (!stack_parameter_count->IsConstant() &&
155
        descriptor_.hint_stack_parameter_count() < 0) {
156
      HInstruction* constant_one = graph()->GetConstant1();
157
      stack_pop_count = AddUncasted<HAdd>(stack_parameter_count, constant_one);
158
      stack_pop_count->ClearFlag(HValue::kCanOverflow);
159 160
      // TODO(mvstanton): verify that stack_parameter_count+1 really fits in a
      // smi.
161
    } else {
162
      int count = descriptor_.hint_stack_parameter_count();
163
      stack_pop_count = Add<HConstant>(count);
164
    }
165 166
  }

167
  if (current_block() != NULL) {
168 169
    HReturn* hreturn_instruction = New<HReturn>(return_value,
                                                stack_pop_count);
170
    FinishCurrentBlock(hreturn_instruction);
171
  }
172 173 174
  return true;
}

175

176 177 178
template <class Stub>
class CodeStubGraphBuilder: public CodeStubGraphBuilderBase {
 public:
179 180
  explicit CodeStubGraphBuilder(CompilationInfo* info, CodeStub* stub)
      : CodeStubGraphBuilderBase(info, stub) {}
181

182 183
  typedef typename Stub::Descriptor Descriptor;

184
 protected:
185
  virtual HValue* BuildCodeStub() {
186
    if (casted_stub()->IsUninitialized()) {
187
      return BuildCodeUninitializedStub();
188 189
    } else {
      return BuildCodeInitializedStub();
190 191 192 193 194 195 196 197 198 199 200
    }
  }

  virtual HValue* BuildCodeInitializedStub() {
    UNIMPLEMENTED();
    return NULL;
  }

  virtual HValue* BuildCodeUninitializedStub() {
    // Force a deopt that falls back to the runtime.
    HValue* undefined = graph()->GetConstantUndefined();
201 202 203
    IfBuilder builder(this);
    builder.IfNot<HCompareObjectEqAndBranch, HValue*>(undefined, undefined);
    builder.Then();
204
    builder.ElseDeopt(DeoptimizeReason::kForcedDeoptToRuntime);
205 206 207
    return undefined;
  }

208 209 210 211
  Stub* casted_stub() { return static_cast<Stub*>(stub()); }
};


212 213
Handle<Code> HydrogenCodeStub::GenerateLightweightMissCode(
    ExternalReference miss) {
214
  Factory* factory = isolate()->factory();
215 216

  // Generate the new code.
217
  MacroAssembler masm(isolate(), NULL, 256, CodeObjectRequired::kYes);
218 219 220

  {
    // Update the static counter each time a new code stub is generated.
221
    isolate()->counters()->code_stubs()->Increment();
222 223 224

    // Generate the code for the stub.
    masm.set_generating_stub(true);
225 226
    // TODO(yangguo): remove this once we can serialize IC stubs.
    masm.enable_serializer();
227
    NoCurrentFrameScope scope(&masm);
228
    GenerateLightweightMiss(&masm, miss);
229 230 231 232 233 234 235 236
  }

  // Create the code object.
  CodeDesc desc;
  masm.GetCode(&desc);

  // Copy the generated code into a heap object.
  Handle<Code> new_object = factory->NewCode(
237
      desc, GetCodeFlags(), masm.CodeObject(), NeedsImmovableCode());
238 239 240
  return new_object;
}

241 242 243
Handle<Code> HydrogenCodeStub::GenerateRuntimeTailCall(
    CodeStubDescriptor* descriptor) {
  const char* name = CodeStub::MajorName(MajorKey());
244
  Zone zone(isolate()->allocator(), ZONE_NAME);
245
  CallInterfaceDescriptor interface_descriptor(GetCallInterfaceDescriptor());
246 247 248
  compiler::CodeAssemblerState state(isolate(), &zone, interface_descriptor,
                                     GetCodeFlags(), name);
  CodeStubAssembler assembler(&state);
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
  int total_params = interface_descriptor.GetStackParameterCount() +
                     interface_descriptor.GetRegisterParameterCount();
  switch (total_params) {
    case 0:
      assembler.TailCallRuntime(descriptor->miss_handler_id(),
                                assembler.Parameter(0));
      break;
    case 1:
      assembler.TailCallRuntime(descriptor->miss_handler_id(),
                                assembler.Parameter(1), assembler.Parameter(0));
      break;
    case 2:
      assembler.TailCallRuntime(descriptor->miss_handler_id(),
                                assembler.Parameter(2), assembler.Parameter(0),
                                assembler.Parameter(1));
      break;
    case 3:
      assembler.TailCallRuntime(descriptor->miss_handler_id(),
                                assembler.Parameter(3), assembler.Parameter(0),
                                assembler.Parameter(1), assembler.Parameter(2));
      break;
    case 4:
      assembler.TailCallRuntime(descriptor->miss_handler_id(),
                                assembler.Parameter(4), assembler.Parameter(0),
                                assembler.Parameter(1), assembler.Parameter(2),
                                assembler.Parameter(3));
      break;
    default:
      UNIMPLEMENTED();
      break;
  }
280
  return compiler::CodeAssembler::GenerateCode(&state);
281
}
282

283
template <class Stub>
284 285
static Handle<Code> DoGenerateCode(Stub* stub) {
  Isolate* isolate = stub->isolate();
286
  CodeStubDescriptor descriptor(stub);
287

288 289 290 291
  if (FLAG_minimal && descriptor.has_miss_handler()) {
    return stub->GenerateRuntimeTailCall(&descriptor);
  }

292
  // If we are uninitialized we can use a light-weight stub to enter
293 294
  // the runtime that is significantly faster than using the standard
  // stub-failure deopt mechanism.
295 296
  if (stub->IsUninitialized() && descriptor.has_miss_handler()) {
    DCHECK(!descriptor.stack_parameter_count().is_valid());
297
    return stub->GenerateLightweightMissCode(descriptor.miss_handler());
298
  }
299
  base::ElapsedTimer timer;
300 301 302
  if (FLAG_profile_hydrogen_code_stub_compilation) {
    timer.Start();
  }
303
  Zone zone(isolate->allocator(), ZONE_NAME);
304 305
  CompilationInfo info(CStrVector(CodeStub::MajorName(stub->MajorKey())),
                       isolate, &zone, stub->GetCodeFlags());
306 307 308 309 310 311 312
  // Parameter count is number of stack parameters.
  int parameter_count = descriptor.GetStackParameterCount();
  if (descriptor.function_mode() == NOT_JS_FUNCTION_STUB_MODE) {
    parameter_count--;
  }
  info.set_parameter_count(parameter_count);
  CodeStubGraphBuilder<Stub> builder(&info, stub);
313
  LChunk* chunk = OptimizeGraph(builder.CreateGraph());
314 315
  Handle<Code> code = chunk->Codegen();
  if (FLAG_profile_hydrogen_code_stub_compilation) {
316 317
    OFStream os(stdout);
    os << "[Lazy compilation of " << stub << " took "
318
       << timer.Elapsed().InMillisecondsF() << " ms]" << std::endl;
319 320
  }
  return code;
321 322
}

323
template <>
324
HValue* CodeStubGraphBuilder<TransitionElementsKindStub>::BuildCodeStub() {
325 326
  ElementsKind const from_kind = casted_stub()->from_kind();
  ElementsKind const to_kind = casted_stub()->to_kind();
327 328
  HValue* const object = GetParameter(Descriptor::kObject);
  HValue* const map = GetParameter(Descriptor::kMap);
329 330 331 332 333

  // The {object} is known to be a JSObject (otherwise it wouldn't have elements
  // anyways).
  object->set_type(HType::JSObject());

334 335
  info()->MarkAsSavesCallerDoubles();

336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
  DCHECK_IMPLIES(IsFastHoleyElementsKind(from_kind),
                 IsFastHoleyElementsKind(to_kind));

  if (AllocationSite::GetMode(from_kind, to_kind) == TRACK_ALLOCATION_SITE) {
    Add<HTrapAllocationMemento>(object);
  }

  if (!IsSimpleMapChangeTransition(from_kind, to_kind)) {
    HInstruction* elements = AddLoadElements(object);

    IfBuilder if_objecthaselements(this);
    if_objecthaselements.IfNot<HCompareObjectEqAndBranch>(
        elements, Add<HConstant>(isolate()->factory()->empty_fixed_array()));
    if_objecthaselements.Then();
    {
      // Determine the elements capacity.
      HInstruction* elements_length = AddLoadFixedArrayLength(elements);

      // Determine the effective (array) length.
      IfBuilder if_objectisarray(this);
      if_objectisarray.If<HHasInstanceTypeAndBranch>(object, JS_ARRAY_TYPE);
      if_objectisarray.Then();
      {
        // The {object} is a JSArray, load the special "length" property.
        Push(Add<HLoadNamedField>(object, nullptr,
                                  HObjectAccess::ForArrayLength(from_kind)));
      }
      if_objectisarray.Else();
      {
        // The {object} is some other JSObject.
        Push(elements_length);
      }
      if_objectisarray.End();
      HValue* length = Pop();

      BuildGrowElementsCapacity(object, elements, from_kind, to_kind, length,
                                elements_length);
    }
    if_objecthaselements.End();
  }

  Add<HStoreNamedField>(object, HObjectAccess::ForMap(), map);
378

379
  return object;
380 381 382
}


383 384
Handle<Code> TransitionElementsKindStub::GenerateCode() {
  return DoGenerateCode(this);
385 386
}

387
template <>
388
HValue* CodeStubGraphBuilder<BinaryOpICStub>::BuildCodeInitializedStub() {
389
  BinaryOpICState state = casted_stub()->state();
390

391 392
  HValue* left = GetParameter(Descriptor::kLeft);
  HValue* right = GetParameter(Descriptor::kRight);
393

394 395 396
  AstType* left_type = state.GetLeftType();
  AstType* right_type = state.GetRightType();
  AstType* result_type = state.GetResultType();
397

398 399
  DCHECK(!left_type->Is(AstType::None()) && !right_type->Is(AstType::None()) &&
         (state.HasSideEffects() || !result_type->Is(AstType::None())));
400 401

  HValue* result = NULL;
402
  HAllocationMode allocation_mode(NOT_TENURED);
403 404 405
  if (state.op() == Token::ADD && (left_type->Maybe(AstType::String()) ||
                                   right_type->Maybe(AstType::String())) &&
      !left_type->Is(AstType::String()) && !right_type->Is(AstType::String())) {
406
    // For the generic add stub a fast case for string addition is performance
407
    // critical.
408
    if (left_type->Maybe(AstType::String())) {
409 410 411 412
      IfBuilder if_leftisstring(this);
      if_leftisstring.If<HIsStringAndBranch>(left);
      if_leftisstring.Then();
      {
413
        Push(BuildBinaryOperation(state.op(), left, right, AstType::String(),
414 415
                                  right_type, result_type,
                                  state.fixed_right_arg(), allocation_mode));
416 417 418
      }
      if_leftisstring.Else();
      {
419 420 421
        Push(BuildBinaryOperation(state.op(), left, right, left_type,
                                  right_type, result_type,
                                  state.fixed_right_arg(), allocation_mode));
422 423
      }
      if_leftisstring.End();
424 425
      result = Pop();
    } else {
426 427 428 429
      IfBuilder if_rightisstring(this);
      if_rightisstring.If<HIsStringAndBranch>(right);
      if_rightisstring.Then();
      {
430
        Push(BuildBinaryOperation(state.op(), left, right, left_type,
431
                                  AstType::String(), result_type,
432
                                  state.fixed_right_arg(), allocation_mode));
433 434 435
      }
      if_rightisstring.Else();
      {
436 437 438
        Push(BuildBinaryOperation(state.op(), left, right, left_type,
                                  right_type, result_type,
                                  state.fixed_right_arg(), allocation_mode));
439 440
      }
      if_rightisstring.End();
441 442 443
      result = Pop();
    }
  } else {
444 445 446
    result = BuildBinaryOperation(state.op(), left, right, left_type,
                                  right_type, result_type,
                                  state.fixed_right_arg(), allocation_mode);
447 448 449 450
  }

  // If we encounter a generic argument, the number conversion is
  // observable, thus we cannot afford to bail out after the fact.
451
  if (!state.HasSideEffects()) {
452 453 454 455 456 457 458
    result = EnforceNumberType(result, result_type);
  }

  return result;
}


459 460
Handle<Code> BinaryOpICStub::GenerateCode() {
  return DoGenerateCode(this);
461 462 463
}


464 465
template <>
HValue* CodeStubGraphBuilder<BinaryOpWithAllocationSiteStub>::BuildCodeStub() {
466
  BinaryOpICState state = casted_stub()->state();
467

468 469 470
  HValue* allocation_site = GetParameter(Descriptor::kAllocationSite);
  HValue* left = GetParameter(Descriptor::kLeft);
  HValue* right = GetParameter(Descriptor::kRight);
471

472 473 474
  AstType* left_type = state.GetLeftType();
  AstType* right_type = state.GetRightType();
  AstType* result_type = state.GetResultType();
475 476
  HAllocationMode allocation_mode(allocation_site);

477 478
  return BuildBinaryOperation(state.op(), left, right, left_type, right_type,
                              result_type, state.fixed_right_arg(),
479
                              allocation_mode);
480 481 482
}


483 484
Handle<Code> BinaryOpWithAllocationSiteStub::GenerateCode() {
  return DoGenerateCode(this);
485 486 487
}


488
template <>
489 490
HValue* CodeStubGraphBuilder<ToBooleanICStub>::BuildCodeInitializedStub() {
  ToBooleanICStub* stub = casted_stub();
491
  IfBuilder if_true(this);
492
  if_true.If<HBranch>(GetParameter(Descriptor::kArgument), stub->hints());
493
  if_true.Then();
494
  if_true.Return(graph()->GetConstantTrue());
495 496
  if_true.Else();
  if_true.End();
497
  return graph()->GetConstantFalse();
498 499
}

500
Handle<Code> ToBooleanICStub::GenerateCode() { return DoGenerateCode(this); }
501

502 503
}  // namespace internal
}  // namespace v8