Home | History | Annotate | Download | only in src
      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 
     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:
     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 
     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:
    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 
    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 
    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>
    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 <>
    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 
    382 Handle<Code> TransitionElementsKindStub::GenerateCode() {
    383   return DoGenerateCode(this);
    384 }
    385 
    386 template <>
    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 
    458 Handle<Code> BinaryOpICStub::GenerateCode() {
    459   return DoGenerateCode(this);
    460 }
    461 
    462 
    463 template <>
    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 
    482 Handle<Code> BinaryOpWithAllocationSiteStub::GenerateCode() {
    483   return DoGenerateCode(this);
    484 }
    485 
    486 
    487 template <>
    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 
    499 Handle<Code> ToBooleanICStub::GenerateCode() { return DoGenerateCode(this); }
    500 
    501 }  // namespace internal
    502 }  // namespace v8
    503