/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=8 sts=4 et sw=4 tw=99:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef jit_shared_BaselineCompiler_shared_h
#define jit_shared_BaselineCompiler_shared_h

#include "jit/BaselineFrameInfo.h"
#include "jit/BaselineIC.h"
#include "jit/BytecodeAnalysis.h"
#include "jit/MacroAssembler.h"

namespace js {
namespace jit {

class BaselineCompilerShared {
 protected:
  JSContext* cx;
  JSScript* script;
  jsbytecode* pc;
  MacroAssembler masm;
  bool ionCompileable_;
  bool compileDebugInstrumentation_;

  TempAllocator& alloc_;
  BytecodeAnalysis analysis_;
  FrameInfo frame;

  FallbackICStubSpace stubSpace_;
  js::Vector<BaselineICEntry, 16, SystemAllocPolicy> icEntries_;

  // Stores the native code offset for a bytecode pc.
  struct PCMappingEntry {
    uint32_t pcOffset;
    uint32_t nativeOffset;
    PCMappingSlotInfo slotInfo;

    // If set, insert a PCMappingIndexEntry before encoding the
    // current entry.
    bool addIndexEntry;
  };

  js::Vector<PCMappingEntry, 16, SystemAllocPolicy> pcMappingEntries_;

  // Labels for the 'movWithPatch' for loading IC entry pointers in
  // the generated IC-calling code in the main jitcode.  These need
  // to be patched with the actual icEntry offsets after the BaselineScript
  // has been allocated.
  struct ICLoadLabel {
    size_t icEntry;
    CodeOffset label;
  };
  js::Vector<ICLoadLabel, 16, SystemAllocPolicy> icLoadLabels_;

  uint32_t pushedBeforeCall_;
#ifdef DEBUG
  bool inCall_;
#endif

  CodeOffset profilerPushToggleOffset_;
  CodeOffset profilerEnterFrameToggleOffset_;
  CodeOffset profilerExitFrameToggleOffset_;

  Vector<CodeOffset> traceLoggerToggleOffsets_;
  CodeOffset traceLoggerScriptTextIdOffset_;

  BaselineCompilerShared(JSContext* cx, TempAllocator& alloc, JSScript* script);

  BaselineICEntry* allocateICEntry(ICStub* stub, ICEntry::Kind kind) {
    if (!stub) return nullptr;

    // Create the entry and add it to the vector.
    if (!icEntries_.append(BaselineICEntry(script->pcToOffset(pc), kind))) {
      ReportOutOfMemory(cx);
      return nullptr;
    }
    BaselineICEntry& vecEntry = icEntries_.back();

    // Set the first stub for the IC entry to the fallback stub
    vecEntry.setFirstStub(stub);

    // Return pointer to the IC entry
    return &vecEntry;
  }

  // Append an ICEntry without a stub.
  bool appendICEntry(ICEntry::Kind kind, uint32_t returnOffset) {
    BaselineICEntry entry(script->pcToOffset(pc), kind);
    entry.setReturnOffset(CodeOffset(returnOffset));
    if (!icEntries_.append(entry)) {
      ReportOutOfMemory(cx);
      return false;
    }
    return true;
  }

  bool addICLoadLabel(CodeOffset label) {
    MOZ_ASSERT(!icEntries_.empty());
    ICLoadLabel loadLabel;
    loadLabel.label = label;
    loadLabel.icEntry = icEntries_.length() - 1;
    if (!icLoadLabels_.append(loadLabel)) {
      ReportOutOfMemory(cx);
      return false;
    }
    return true;
  }

  JSFunction* function() const {
    // Not delazifying here is ok as the function is guaranteed to have
    // been delazified before compilation started.
    return script->functionNonDelazifying();
  }

  ModuleObject* module() const { return script->module(); }

  PCMappingSlotInfo getStackTopSlotInfo() {
    MOZ_ASSERT(frame.numUnsyncedSlots() <= 2);
    switch (frame.numUnsyncedSlots()) {
      case 0:
        return PCMappingSlotInfo::MakeSlotInfo();
      case 1:
        return PCMappingSlotInfo::MakeSlotInfo(
            PCMappingSlotInfo::ToSlotLocation(frame.peek(-1)));
      case 2:
      default:
        return PCMappingSlotInfo::MakeSlotInfo(
            PCMappingSlotInfo::ToSlotLocation(frame.peek(-1)),
            PCMappingSlotInfo::ToSlotLocation(frame.peek(-2)));
    }
  }

  template <typename T>
  void pushArg(const T& t) {
    masm.Push(t);
  }
  void prepareVMCall();

  enum CallVMPhase { POST_INITIALIZE, PRE_INITIALIZE, CHECK_OVER_RECURSED };
  bool callVM(const VMFunction& fun, CallVMPhase phase = POST_INITIALIZE);

  bool callVMNonOp(const VMFunction& fun, CallVMPhase phase = POST_INITIALIZE) {
    if (!callVM(fun, phase)) return false;
    icEntries_.back().setFakeKind(ICEntry::Kind_NonOpCallVM);
    return true;
  }

 public:
  BytecodeAnalysis& analysis() { return analysis_; }

  void setCompileDebugInstrumentation() { compileDebugInstrumentation_ = true; }
};

}  // namespace jit
}  // namespace js

#endif /* jit_shared_BaselineCompiler_shared_h */
