/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * 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/. */

#include "jit/JitHints-inl.h"
#include "vm/JSScript-inl.h"

using namespace js;
using namespace js::jit;

JitHintsMap::~JitHintsMap() {
  while (!ionHintQueue_.isEmpty()) {
    IonHint* e = ionHintQueue_.popFirst();
    js_delete(e);
  }
  ionHintMap_.clear();
}

JitHintsMap::IonHint* JitHintsMap::addIonHint(ScriptKey key,
                                              ScriptToHintMap::AddPtr& p) {
  if (ionHintMap_.count() >= IonHintMaxEntries) {
    IonHint* h = ionHintQueue_.popFirst();
    ionHintMap_.remove(h->key());
    js_delete(h);
  }

  IonHint* hint = js_new<IonHint>(key);
  if (!hint) {
    return nullptr;
  }

  if (!ionHintMap_.add(p, key, hint)) {
    return nullptr;
  }

  ionHintQueue_.insertBack(hint);
  return hint;
}

void JitHintsMap::updateAsRecentlyUsed(IonHint* hint) {
  hint->remove();
  ionHintQueue_.insertBack(hint);
}

bool JitHintsMap::recordIonCompilation(JSScript* script) {
  ScriptKey key = getScriptKey(script);
  if (!key) {
    return true;
  }

  auto p = ionHintMap_.lookupForAdd(key);
  IonHint* hint = nullptr;
  if (p) {
    // Don't modify existing threshold values.
    hint = p->value();
    updateAsRecentlyUsed(hint);
  } else {
    hint = addIonHint(key, p);
    if (!hint) {
      return false;
    }
  }

  return true;
}

bool JitHintsMap::getIonThresholdHint(JSScript* script,
                                      uint32_t& thresholdOut) {
  ScriptKey key = getScriptKey(script);
  if (key) {
    auto p = ionHintMap_.lookup(key);
    if (p) {
      IonHint* hint = p->value();
      updateAsRecentlyUsed(hint);
      thresholdOut = hint->threshold();
      return true;
    }
  }

  return false;
}

void JitHintsMap::recordInvalidation(JSScript* script) {
  ScriptKey key = getScriptKey(script);
  if (key) {
    auto p = ionHintMap_.lookup(key);
    if (p) {
      p->value()->incThreshold(InvalidationThresholdIncrement);
    }
  }
}
