text: // Copyright 2014 the V8 project authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "src/codegen/code-factory.h"
  
  #include "src/builtins/builtins-descriptors.h"
  #include "src/ic/ic.h"
  #include "src/init/bootstrapper.h"
  #include "src/objects/allocation-site-inl.h"
  #include "src/objects/objects-inl.h"
  
  namespace v8 {
  namespace internal {
  
  // static
  Handle<Code> CodeFactory::RuntimeCEntry(Isolate* isolate, int result_size) {
    return CodeFactory::CEntry(isolate, result_size);
  }
  
  #define CENTRY_CODE(RS, SD, AM, BE) \
    BUILTIN_CODE(isolate, CEntry_##RS##_##SD##_##AM##_##BE)
  
  // static
  Handle<Code> CodeFactory::CEntry(Isolate* isolate, int result_size,
                                   SaveFPRegsMode save_doubles,
                                   ArgvMode argv_mode, bool builtin_exit_frame) {
    // Aliases for readability below.
    const int rs = result_size;
    const SaveFPRegsMode sd = save_doubles;
    const ArgvMode am = argv_mode;
    const bool be = builtin_exit_frame;
  
    if (rs == 1 && sd == kDontSaveFPRegs && am == kArgvOnStack && !be) {
      return CENTRY_CODE(Return1, DontSaveFPRegs, ArgvOnStack, NoBuiltinExit);
    } else if (rs == 1 && sd == kDontSaveFPRegs && am == kArgvOnStack && be) {
      return CENTRY_CODE(Return1, DontSaveFPRegs, ArgvOnStack, BuiltinExit);
    } else if (rs == 1 && sd == kDontSaveFPRegs && am == kArgvInRegister && !be) {
      return CENTRY_CODE(Return1, DontSaveFPRegs, ArgvInRegister, NoBuiltinExit);
    } else if (rs == 1 && sd == kSaveFPRegs && am == kArgvOnStack && !be) {
      return CENTRY_CODE(Return1, SaveFPRegs, ArgvOnStack, NoBuiltinExit);
    } else if (rs == 1 && sd == kSaveFPRegs && am == kArgvOnStack && be) {
      return CENTRY_CODE(Return1, SaveFPRegs, ArgvOnStack, BuiltinExit);
    } else if (rs == 2 && sd == kDontSaveFPRegs && am == kArgvOnStack && !be) {
      return CENTRY_CODE(Return2, DontSaveFPRegs, ArgvOnStack, NoBuiltinExit);
    } else if (rs == 2 && sd == kDontSaveFPRegs && am == kArgvOnStack && be) {
      return CENTRY_CODE(Return2, DontSaveFPRegs, ArgvOnStack, BuiltinExit);
    } else if (rs == 2 && sd == kDontSaveFPRegs && am == kArgvInRegister && !be) {
      return CENTRY_CODE(Return2, DontSaveFPRegs, ArgvInRegister, NoBuiltinExit);
    } else if (rs == 2 && sd == kSaveFPRegs && am == kArgvOnStack && !be) {
      return CENTRY_CODE(Return2, SaveFPRegs, ArgvOnStack, NoBuiltinExit);
    } else if (rs == 2 && sd == kSaveFPRegs && am == kArgvOnStack && be) {
      return CENTRY_CODE(Return2, SaveFPRegs, ArgvOnStack, BuiltinExit);
    }
  
    UNREACHABLE();
  }
  
  #undef CENTRY_CODE
  
  // static
  Callable CodeFactory::ApiGetter(Isolate* isolate) {
    return Builtins::CallableFor(isolate, Builtins::kCallApiGetter);
  }
  
  // static
  Callable CodeFactory::CallApiCallback(Isolate* isolate) {
    return Builtins::CallableFor(isolate, Builtins::kCallApiCallback);
  }
  
  // static
  Callable CodeFactory::LoadGlobalIC(Isolate* isolate, TypeofMode typeof_mode) {
    return typeof_mode == NOT_INSIDE_TYPEOF
               ? Builtins::CallableFor(isolate, Builtins::kLoadGlobalICTrampoline)
               : Builtins::CallableFor(
                     isolate, Builtins::kLoadGlobalICInsideTypeofTrampoline);
  }
  
  // static
  Callable CodeFactory::LoadGlobalICInOptimizedCode(Isolate* isolate,
                                                    TypeofMode typeof_mode) {
    return typeof_mode == NOT_INSIDE_TYPEOF
               ? Builtins::CallableFor(isolate, Builtins::kLoadGlobalIC)
               : Builtins::CallableFor(isolate,
                                       Builtins::kLoadGlobalICInsideTypeof);
  }
  
  Callable CodeFactory::StoreOwnIC(Isolate* isolate) {
    // TODO(ishell): Currently we use StoreOwnIC only for storing properties that
    // already exist in the boilerplate therefore we can use StoreIC.
    return Builtins::CallableFor(isolate, Builtins::kStoreICTrampoline);
  }
  
  Callable CodeFactory::StoreOwnICInOptimizedCode(Isolate* isolate) {
    // TODO(ishell): Currently we use StoreOwnIC only for storing properties that
    // already exist in the boilerplate therefore we can use StoreIC.
    return Builtins::CallableFor(isolate, Builtins::kStoreIC);
  }
  
  Callable CodeFactory::KeyedStoreIC_SloppyArguments(Isolate* isolate,
                                                     KeyedAccessStoreMode mode) {
    Builtins::Name builtin_index;
    switch (mode) {
      case STANDARD_STORE:
        builtin_index = Builtins::kKeyedStoreIC_SloppyArguments_Standard;
        break;
      case STORE_AND_GROW_HANDLE_COW:
        builtin_index =
            Builtins::kKeyedStoreIC_SloppyArguments_GrowNoTransitionHandleCOW;
        break;
      case STORE_IGNORE_OUT_OF_BOUNDS:
        builtin_index =
            Builtins::kKeyedStoreIC_SloppyArguments_NoTransitionIgnoreOOB;
        break;
      case STORE_HANDLE_COW:
        builtin_index =
            Builtins::kKeyedStoreIC_SloppyArguments_NoTransitionHandleCOW;
        break;
      default:
        UNREACHABLE();
    }
    return isolate->builtins()->CallableFor(isolate, builtin_index);
  }
  
  Callable CodeFactory::KeyedStoreIC_Slow(Isolate* isolate,
                                          KeyedAccessStoreMode mode) {
    Builtins::Name builtin_index;
    switch (mode) {
      case STANDARD_STORE:
        builtin_index = Builtins::kKeyedStoreIC_Slow_Standard;
        break;
      case STORE_AND_GROW_HANDLE_COW:
        builtin_index = Builtins::kKeyedStoreIC_Slow_GrowNoTransitionHandleCOW;
        break;
      case STORE_IGNORE_OUT_OF_BOUNDS:
        builtin_index = Builtins::kKeyedStoreIC_Slow_NoTransitionIgnoreOOB;
        break;
      case STORE_HANDLE_COW:
        builtin_index = Builtins::kKeyedStoreIC_Slow_NoTransitionHandleCOW;
        break;
      default:
        UNREACHABLE();
    }
    return isolate->builtins()->CallableFor(isolate, builtin_index);
  }
  
  Callable CodeFactory::StoreInArrayLiteralIC_Slow(Isolate* isolate,
                                                   KeyedAccessStoreMode mode) {
    Builtins::Name builtin_index;
    switch (mode) {
      case STANDARD_STORE:
        builtin_index = Builtins::kStoreInArrayLiteralIC_Slow_Standard;
        break;
      case STORE_AND_GROW_HANDLE_COW:
        builtin_index =
            Builtins::kStoreInArrayLiteralIC_Slow_GrowNoTransitionHandleCOW;
        break;
      case STORE_IGNORE_OUT_OF_BOUNDS:
        builtin_index =
            Builtins::kStoreInArrayLiteralIC_Slow_NoTransitionIgnoreOOB;
        break;
      case STORE_HANDLE_COW:
        builtin_index =
            Builtins::kStoreInArrayLiteralIC_Slow_NoTransitionHandleCOW;
        break;
      default:
        UNREACHABLE();
    }
    return isolate->builtins()->CallableFor(isolate, builtin_index);
  }
  
  Callable CodeFactory::ElementsTransitionAndStore(Isolate* isolate,
                                                   KeyedAccessStoreMode mode) {
    Builtins::Name builtin_index;
    switch (mode) {
      case STANDARD_STORE:
        builtin_index = Builtins::kElementsTransitionAndStore_Standard;
        break;
      case STORE_AND_GROW_HANDLE_COW:
        builtin_index =
            Builtins::kElementsTransitionAndStore_GrowNoTransitionHandleCOW;
        break;
      case STORE_IGNORE_OUT_OF_BOUNDS:
        builtin_index =
            Builtins::kElementsTransitionAndStore_NoTransitionIgnoreOOB;
        break;
      case STORE_HANDLE_COW:
        builtin_index =
            Builtins::kElementsTransitionAndStore_NoTransitionHandleCOW;
        break;
      default:
        UNREACHABLE();
    }
    return isolate->builtins()->CallableFor(isolate, builtin_index);
  }
  
  Callable CodeFactory::StoreFastElementIC(Isolate* isolate,
                                           KeyedAccessStoreMode mode) {
    Builtins::Name builtin_index;
    switch (mode) {
      case STANDARD_STORE:
        builtin_index = Builtins::kStoreFastElementIC_Standard;
        break;
      case STORE_AND_GROW_HANDLE_COW:
        builtin_index = Builtins::kStoreFastElementIC_GrowNoTransitionHandleCOW;
        break;
      case STORE_IGNORE_OUT_OF_BOUNDS:
        builtin_index = Builtins::kStoreFastElementIC_NoTransitionIgnoreOOB;
        break;
      case STORE_HANDLE_COW:
        builtin_index = Builtins::kStoreFastElementIC_NoTransitionHandleCOW;
        break;
      default:
        UNREACHABLE();
    }
    return isolate->builtins()->CallableFor(isolate, builtin_index);
  }
  
  // static
  Callable CodeFactory::BinaryOperation(Isolate* isolate, Operation op) {
    switch (op) {
      case Operation::kShiftRight:
        return Builtins::CallableFor(isolate, Builtins::kShiftRight);
      case Operation::kShiftLeft:
        return Builtins::CallableFor(isolate, Builtins::kShiftLeft);
      case Operation::kShiftRightLogical:
        return Builtins::CallableFor(isolate, Builtins::kShiftRightLogical);
      case Operation::kAdd:
        return Builtins::CallableFor(isolate, Builtins::kAdd);
      case Operation::kSubtract:
        return Builtins::CallableFor(isolate, Builtins::kSubtract);
      case Operation::kMultiply:
        return Builtins::CallableFor(isolate, Builtins::kMultiply);
      case Operation::kDivide:
        return Builtins::CallableFor(isolate, Builtins::kDivide);
      case Operation::kModulus:
        return Builtins::CallableFor(isolate, Builtins::kModulus);
      case Operation::kBitwiseOr:
        return Builtins::CallableFor(isolate, Builtins::kBitwiseOr);
      case Operation::kBitwiseAnd:
        return Builtins::CallableFor(isolate, Builtins::kBitwiseAnd);
      case Operation::kBitwiseXor:
        return Builtins::CallableFor(isolate, Builtins::kBitwiseXor);
      default:
        break;
    }
    UNREACHABLE();
  }
  
  // static
  Callable CodeFactory::NonPrimitiveToPrimitive(Isolate* isolate,
                                                ToPrimitiveHint hint) {
    return Callable(isolate->builtins()->NonPrimitiveToPrimitive(hint),
                    TypeConversionDescriptor{});
  }
  
  // static
  Callable CodeFactory::OrdinaryToPrimitive(Isolate* isolate,
                                            OrdinaryToPrimitiveHint hint) {
    return Callable(isolate->builtins()->OrdinaryToPrimitive(hint),
                    TypeConversionDescriptor{});
  }
  
  // static
  Callable CodeFactory::StringAdd(Isolate* isolate, StringAddFlags flags) {
    switch (flags) {
      case STRING_ADD_CHECK_NONE:
        return Builtins::CallableFor(isolate, Builtins::kStringAdd_CheckNone);
      case STRING_ADD_CONVERT_LEFT:
        return Builtins::CallableFor(isolate, Builtins::kStringAdd_ConvertLeft);
      case STRING_ADD_CONVERT_RIGHT:
        return Builtins::CallableFor(isolate, Builtins::kStringAdd_ConvertRight);
    }
    UNREACHABLE();
  }
  
  // static
  Callable CodeFactory::ResumeGenerator(Isolate* isolate) {
    return Builtins::CallableFor(isolate, Builtins::kResumeGeneratorTrampoline);
  }
  
  // static
  Callable CodeFactory::FrameDropperTrampoline(Isolate* isolate) {
    return Builtins::CallableFor(isolate, Builtins::kFrameDropperTrampoline);
  }
  
  // static
  Callable CodeFactory::HandleDebuggerStatement(Isolate* isolate) {
    return Builtins::CallableFor(isolate, Builtins::kHandleDebuggerStatement);
  }
  
  // static
  Callable CodeFactory::FastNewFunctionContext(Isolate* isolate,
                                               ScopeType scope_type) {
    switch (scope_type) {
      case ScopeType::EVAL_SCOPE:
        return Builtins::CallableFor(isolate,
                                     Builtins::kFastNewFunctionContextEval);
      case ScopeType::FUNCTION_SCOPE:
        return Builtins::CallableFor(isolate,
                                     Builtins::kFastNewFunctionContextFunction);
      default:
        UNREACHABLE();
    }
  }
  
  // static
  Callable CodeFactory::ArgumentAdaptor(Isolate* isolate) {
    return Builtins::CallableFor(isolate, Builtins::kArgumentsAdaptorTrampoline);
  }
  
  // static
  Callable CodeFactory::Call(Isolate* isolate, ConvertReceiverMode mode) {
    return Callable(isolate->builtins()->Call(mode), CallTrampolineDescriptor{});
  }
  
  // static
  Callable CodeFactory::CallWithArrayLike(Isolate* isolate) {
    return Builtins::CallableFor(isolate, Builtins::kCallWithArrayLike);
  }
  
  // static
  Callable CodeFactory::CallWithSpread(Isolate* isolate) {
    return Builtins::CallableFor(isolate, Builtins::kCallWithSpread);
  }
  
  // static
  Callable CodeFactory::CallFunction(Isolate* isolate, ConvertReceiverMode mode) {
    return Callable(isolate->builtins()->CallFunction(mode),
                    CallTrampolineDescriptor{});
  }
  
  // static
  Callable CodeFactory::CallVarargs(Isolate* isolate) {
    return Builtins::CallableFor(isolate, Builtins::kCallVarargs);
  }
  
  // static
  Callable CodeFactory::CallForwardVarargs(Isolate* isolate) {
    return Builtins::CallableFor(isolate, Builtins::kCallForwardVarargs);
  }
  
  // static
  Callable CodeFactory::CallFunctionForwardVarargs(Isolate* isolate) {
    return Builtins::CallableFor(isolate, Builtins::kCallFunctionForwardVarargs);
  }
  
  // static
  Callable CodeFactory::Construct(Isolate* isolate) {
    return Builtins::CallableFor(isolate, Builtins::kConstruct);
  }
  
  // static
  Callable CodeFactory::ConstructWithSpread(Isolate* isolate) {
    return Builtins::CallableFor(isolate, Builtins::kConstructWithSpread);
  }
  
  // static
  Callable CodeFactory::ConstructFunction(Isolate* isolate) {
    return Builtins::CallableFor(isolate, Builtins::kConstructFunction);
  }
  
  // static
  Callable CodeFactory::ConstructVarargs(Isolate* isolate) {
    return Builtins::CallableFor(isolate, Builtins::kConstructVarargs);
  }
  
  // static
  Callable CodeFactory::ConstructForwardVarargs(Isolate* isolate) {
    return Builtins::CallableFor(isolate, Builtins::kConstructForwardVarargs);
  }
  
  // static
  Callable CodeFactory::ConstructFunctionForwardVarargs(Isolate* isolate) {
    return Builtins::CallableFor(isolate,
                                 Builtins::kConstructFunctionForwardVarargs);
  }
  
  // static
  Callable CodeFactory::InterpreterPushArgsThenCall(
      Isolate* isolate, ConvertReceiverMode receiver_mode,
      InterpreterPushArgsMode mode) {
    switch (mode) {
      case InterpreterPushArgsMode::kArrayFunction:
        // There is no special-case handling of calls to Array. They will all go
        // through the kOther case below.
        UNREACHABLE();
      case InterpreterPushArgsMode::kWithFinalSpread:
        return Builtins::CallableFor(
            isolate, Builtins::kInterpreterPushArgsThenCallWithFinalSpread);
      case InterpreterPushArgsMode::kOther:
        switch (receiver_mode) {
          case ConvertReceiverMode::kNullOrUndefined:
            return Builtins::CallableFor(
                isolate, Builtins::kInterpreterPushUndefinedAndArgsThenCall);
          case ConvertReceiverMode::kNotNullOrUndefined:
          case ConvertReceiverMode::kAny:
            return Builtins::CallableFor(isolate,
                                         Builtins::kInterpreterPushArgsThenCall);
        }
    }
    UNREACHABLE();
  }
  
  // static
  Callable CodeFactory::InterpreterPushArgsThenConstruct(
      Isolate* isolate, InterpreterPushArgsMode mode) {
    switch (mode) {
      case InterpreterPushArgsMode::kArrayFunction:
        return Builtins::CallableFor(
            isolate, Builtins::kInterpreterPushArgsThenConstructArrayFunction);
      case InterpreterPushArgsMode::kWithFinalSpread:
        return Builtins::CallableFor(
            isolate, Builtins::kInterpreterPushArgsThenConstructWithFinalSpread);
      case InterpreterPushArgsMode::kOther:
        return Builtins::CallableFor(isolate,
                                     Builtins::kInterpreterPushArgsThenConstruct);
    }
    UNREACHABLE();
  }
  
  // static
  Callable CodeFactory::InterpreterCEntry(Isolate* isolate, int result_size) {
    // Note: If we ever use fpregs in the interpreter then we will need to
    // save fpregs too.
    Handle<Code> code = CodeFactory::CEntry(isolate, result_size, kDontSaveFPRegs,
                                            kArgvInRegister);
    if (result_size == 1) {
      return Callable(code, InterpreterCEntry1Descriptor{});
    } else {
      DCHECK_EQ(result_size, 2);
      return Callable(code, InterpreterCEntry2Descriptor{});
    }
  }
  
  // static
  Callable CodeFactory::InterpreterOnStackReplacement(Isolate* isolate) {
    return Builtins::CallableFor(isolate,
                                 Builtins::kInterpreterOnStackReplacement);
  }
  
  // static
  Callable CodeFactory::ArrayNoArgumentConstructor(
      Isolate* isolate, ElementsKind kind,
      AllocationSiteOverrideMode override_mode) {
  #define CASE(kind_caps, kind_camel, mode_camel) \
    case kind_caps:                               \
      return Builtins::CallableFor(               \
          isolate,                                \
          Builtins::kArrayNoArgumentConstructor_##kind_camel##_##mode_camel);
    if (override_mode == DONT_OVERRIDE && AllocationSite::ShouldTrack(kind)) {
      DCHECK(IsSmiElementsKind(kind));
      switch (kind) {
        CASE(PACKED_SMI_ELEMENTS, PackedSmi, DontOverride);
        CASE(HOLEY_SMI_ELEMENTS, HoleySmi, DontOverride);
        default:
          UNREACHABLE();
      }
    } else {
      DCHECK(override_mode == DISABLE_ALLOCATION_SITES ||
             !AllocationSite::ShouldTrack(kind));
      switch (kind) {
        CASE(PACKED_SMI_ELEMENTS, PackedSmi, DisableAllocationSites);
        CASE(HOLEY_SMI_ELEMENTS, HoleySmi, DisableAllocationSites);
        CASE(PACKED_ELEMENTS, Packed, DisableAllocationSites);
        CASE(HOLEY_ELEMENTS, Holey, DisableAllocationSites);
        CASE(PACKED_DOUBLE_ELEMENTS, PackedDouble, DisableAllocationSites);
        CASE(HOLEY_DOUBLE_ELEMENTS, HoleyDouble, DisableAllocationSites);
        default:
          UNREACHABLE();
      }
    }
  #undef CASE
  }
  
  // static
  Callable CodeFactory::ArraySingleArgumentConstructor(
      Isolate* isolate, ElementsKind kind,
      AllocationSiteOverrideMode override_mode) {
  #define CASE(kind_caps, kind_camel, mode_camel) \
    case kind_caps:                               \
      return Builtins::CallableFor(               \
          isolate,                                \
          Builtins::kArraySingleArgumentConstructor_##kind_camel##_##mode_camel)
    if (override_mode == DONT_OVERRIDE && AllocationSite::ShouldTrack(kind)) {
      DCHECK(IsSmiElementsKind(kind));
      switch (kind) {
        CASE(PACKED_SMI_ELEMENTS, PackedSmi, DontOverride);
        CASE(HOLEY_SMI_ELEMENTS, HoleySmi, DontOverride);
        default:
          UNREACHABLE();
      }
    } else {
      DCHECK(override_mode == DISABLE_ALLOCATION_SITES ||
             !AllocationSite::ShouldTrack(kind));
      switch (kind) {
        CASE(PACKED_SMI_ELEMENTS, PackedSmi, DisableAllocationSites);
        CASE(HOLEY_SMI_ELEMENTS, HoleySmi, DisableAllocationSites);
        CASE(PACKED_ELEMENTS, Packed, DisableAllocationSites);
        CASE(HOLEY_ELEMENTS, Holey, DisableAllocationSites);
        CASE(PACKED_DOUBLE_ELEMENTS, PackedDouble, DisableAllocationSites);
        CASE(HOLEY_DOUBLE_ELEMENTS, HoleyDouble, DisableAllocationSites);
        default:
          UNREACHABLE();
      }
    }
  #undef CASE
  }
  
  }  // namespace internal
  }  // namespace v8
  