/*
 * Copyright 2021 Mozilla Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef wasm_instance_data_h
#define wasm_instance_data_h

#include <stdint.h>

#include "NamespaceImports.h"

#include "gc/Pretenuring.h"
#include "js/Utility.h"
#include "vm/JSFunction.h"
#include "wasm/WasmInstance.h"
#include "wasm/WasmMemory.h"
#include "wasm/WasmTypeDecls.h"

namespace js {
namespace wasm {

// ExportArg holds the unboxed operands to the wasm entry trampoline which can
// be called through an ExportFuncPtr.

struct ExportArg {
  uint64_t lo;
  uint64_t hi;
};

using ExportFuncPtr = int32_t (*)(ExportArg*, Instance*);

// TypeDefInstanceData describes the runtime information associated with a
// module's type definition. This is accessed directly from JIT code and the
// Instance.

struct TypeDefInstanceData {
  TypeDefInstanceData()
      : typeDef(nullptr),
        superTypeVector(nullptr),
        shape(nullptr),
        clasp(nullptr) {
    memset(&cached, 0, sizeof(cached));
    cached.strukt.allocKind = gc::AllocKind::INVALID;
  }

  // The canonicalized pointer to this type definition. This is kept alive by
  // the type context associated with the instance.
  const wasm::TypeDef* typeDef;

  // The supertype vector for this type definition.  This is also kept alive
  // by the type context associated with the instance.
  const wasm::SuperTypeVector* superTypeVector;

  // The next three fields are only meaningful for, and used by, structs and
  // arrays.
  GCPtr<Shape*> shape;
  const JSClass* clasp;

  // This union is only meaningful for structs and arrays, and should otherwise
  // be zeroed out.  It exists so that allocators of structs and arrays don't
  // need to chase through `typeDef` to find this info.
  union {
    struct {
      // When `typeDef` refers to a struct type, these are copied unchanged
      // from fields of the same name in StructType.
      uint32_t payloadOffsetIL;
      uint32_t totalSizeIL;
      uint32_t totalSizeOOL;
      uint32_t oolPointerOffset;
      // Copied from StructType, and updated by GetFinalizedAllocKindForClass
      // (see comment on StructType::allocKind_).
      gc::AllocKind allocKind;
    } strukt;
    struct {
      // When `typeDef` refers to an array type, this caches the value of
      // `typeDef->arrayType().fieldType_.size()` (a size in bytes).
      uint32_t elemSize;
    } array;
  } cached;

  static constexpr size_t offsetOfShape() {
    return offsetof(TypeDefInstanceData, shape);
  }
  static constexpr size_t offsetOfSuperTypeVector() {
    return offsetof(TypeDefInstanceData, superTypeVector);
  }
  static constexpr size_t offsetOfArrayElemSize() {
    return offsetof(TypeDefInstanceData, cached.array.elemSize);
  }
};

// FuncDefInstanceData maintains the per-instance hotness state for a locally
// defined wasm function.  This is a signed-int32 value that counts downwards
// from an initially non-negative value.  At the point where the value
// transitions below zero (not *to* zero), we deem the owning function to
// have become hot.  Transitions from one negative value to any other (even
// more) negative value are meaningless and should not happen.
struct FuncDefInstanceData {
  int32_t hotnessCounter;
};

// FuncExportInstanceData maintains the exported function JS wrapper for an
// exported function.
struct FuncExportInstanceData {
  GCPtr<JSFunction*> func;
};

// FuncImportInstanceData describes the region of wasm global memory allocated
// in the instance's thread-local storage for a function import. This is
// accessed directly from JIT code and mutated by Instance as exits become
// optimized and deoptimized.

struct FuncImportInstanceData {
  // The code to call at an import site: a wasm callee, a thunk into C++, or a
  // thunk into JIT code.
  void* code;

  // The callee's Instance pointer, which must be loaded to InstanceReg
  // (along with any pinned registers) before calling 'code'.
  Instance* instance;

  // The callee function's realm.
  JS::Realm* realm;

  // A GC pointer which keeps the callee alive and is used to recover import
  // values for lazy table initialization.
  GCPtr<JSObject*> callable;
  static_assert(sizeof(GCPtr<JSObject*>) == sizeof(void*), "for JIT access");

  // See "Wasm Function.prototype.call.bind optimization" in WasmInstance.cpp
  // for more information.
  bool isFunctionCallBind;
};

struct MemoryInstanceData {
  // Pointer to the memory object.
  GCPtr<WasmMemoryObject*> memory;

  // Pointer to the base of the memory.
  uint8_t* base;

  // Bounds check limit in bytes. This is 64 bits on 64-bit systems so as to
  // allow for heap lengths up to and beyond 4GB, and 32 bits on 32-bit systems,
  // where heaps are limited to 2GB.
  //
  // See "Linear memory addresses and bounds checking" in WasmMemory.cpp.
  uintptr_t boundsCheckLimit;

  // The default boundsCheckLimit is used for standard page sizes and also 8-bit
  // memory accesses on custom page sizes. These other limits are only used for
  // accesses on memories with custom page sizes.
#ifdef ENABLE_WASM_CUSTOM_PAGE_SIZES
  uintptr_t boundsCheckLimit16;
  uintptr_t boundsCheckLimit32;
  uintptr_t boundsCheckLimit64;
  uintptr_t boundsCheckLimit128;
#endif

  // Whether this memory is shared or not.
  bool isShared;

  // Total mapped size of the memory buffer, including guard pages. Stored
  // here to avoid touching GC objects from signal handlers.
  size_t mappedSize;
};

// TableInstanceData describes the region of wasm global memory allocated in the
// instance's thread-local storage which is accessed directly from JIT code
// to bounds-check and index the table.

struct TableInstanceData {
  // Length of the table in number of elements (not bytes). Although the type is
  // uint64_t, the maximum value fits in 32 bits -- this value can safely be
  // loaded as either a 32-bit value or a 64-bit value.
  uint64_t length;

  // Pointer to the array of elements (which can have various representations).
  // For tables of anyref this is null.
  // For tables of functions, this is a pointer to the array of code pointers.
  void* elements;
};

// TagInstanceData describes the instance state associated with a tag.

struct TagInstanceData {
  GCPtr<WasmTagObject*> object;
};

// Table element for TableRepr::Func which carries both the code pointer and
// a instance pointer (and thus anything reachable through the instance).

struct FunctionTableElem {
  // The code to call when calling this element. The table ABI is the system
  // ABI with the additional ABI requirements that:
  //  - InstanceReg and any pinned registers have been loaded appropriately
  //  - if this is a heterogeneous table that requires a signature check,
  //    WasmTableCallSigReg holds the signature id.
  void* code;

  // The pointer to the callee's instance's Instance. This must be loaded into
  // InstanceReg before calling 'code'.
  Instance* instance;
};

// A collection of metrics for a `call_ref` instruction. This is tracked by
// baseline when we are using lazy tiering to perform speculative inlining.
//
// See MacroAssembler::updateCallRefMetrics for how this is written into.
//
// Because it contains thread-local data and is written into without
// synchronization, we cannot access this directly from our function compilers
// and so we use CallRefHints for that (see WasmModuleTypes.h).
struct CallRefMetrics {
  // We track up to NUM_SLOTS targets with associated count, plus a count for
  // "all other" targets, including cross-instance calls.  This facilitates
  // knowing the total number of calls made by the instruction, which is
  // required in order to know whether the hottest tracked function is more
  // than some percentage (eg, 50%) of all calls.
  //
  // In order to keep search costs low (in the baseline code), we rely on the
  // fact that most call sites have distributions which are heavily skewed
  // towards one target.  This struct is updated by the code generated in
  // GenerateUpdateCallRefMetricsStub.  That tries to make
  // `targets[0]`/`counts[0]` be the hottest target, so that for most calls,
  // the monitoring code will only check `targets[0]` for a match.
  //
  // For NUM_SLOTS <= 2, GenerateUpdateCallRefMetricsStub's incremental-sort
  // heuristic maintains the `counts` array in strictly non-decreasing order.
  // For NUM_SLOTS > 2, in the worst case we will have counts[N+1] at most
  // (NUM_SLOTS - 2) larger than counts[N].  In practice the incremental
  // sorting heuristic is very effective and so counts are in decreasing order,
  // as we desire.
  //
  // Once NUM_SLOTS targets are being tracked, all new targets will be lumped
  // together in the `countOther` bucket.  This can lead to the unfortunate
  // case of having NUM_SLOTS different cold targets show up first, after which
  // follows a different target that is hot, but cannot be inlined because it
  // goes in the `countOther` bucket, so its identity is unknown.  This is
  // unlikely but could happen.  The only known fix is to increase NUM_SLOTS.
  //
  // The `targets` values may be nullptr only to indicate that the slot is not
  // in use.  No legitimate target can be nullptr.  Given that the state is
  // updated by generated code and that code isn't entirely simple, we place
  // emphasis on checking invariants carefully.
  //
  // Stores of funcrefs in `targets[]`: These CallRefMetrics structs logically
  // belong to the Instance data, and do not require any GC barriers for two
  // reasons:
  //
  // 1. The pre-write barrier protects against an unmarked object being stored
  //    into a marked object during an incremental GC.  However this funcref is
  //    from the Instance we're storing it into (see above) and so if the
  //    instance has already been traced, this function will already have been
  //    traced (all exported functions are kept alive by an instance cache).
  //
  // 2. The post-write barrier tracks edges from tenured objects to nursery
  //    objects.  However wasm exported functions are not nursery allocated and
  //    so no new edge can be created.
  //
  // Overflows in `counts[]` and `countOther`: increments of these values are
  // not checked for overflow and so could wrap around from 2^32-1 to zero.
  // We ignore but tolerate this, because:
  //
  // 1. This is extremely unlikely to happen in practice, since the function
  //    containing the call site is almost certain to get tiered up long before
  //    any of these counters gets anywhere near the limit.
  //
  // 2. Performing saturating increments is possible, but has a minimum extra
  //    cost of two instructions, and given (1.) it is pointless.
  //
  // This does however require that interpretation of the `counts[]` and
  // `countOther` values needs to be aware that zeroes could mean 2^32 or any
  // multiple of it.  Hence a zero in `counts[]` does not necessarily mean that
  // the corresponding `target[]` was never called, nor is it the case that a
  // `countsOther` of zero means no "other" targets were observed.

  static constexpr size_t NUM_SLOTS = 3;
  static_assert(NUM_SLOTS >= 1);  // 1 slot + others is the minimal config

  // An array of pairs of (target, count) ..
  GCPtr<JSFunction*> targets[NUM_SLOTS];
  uint32_t counts[NUM_SLOTS];
  // .. and a count for all other targets.
  uint32_t countOther;

  // Generated code assumes this
  static_assert(sizeof(GCPtr<JSFunction*>) == sizeof(void*));
  static_assert(sizeof(uint32_t) == 4);

  CallRefMetrics() {
    for (size_t i = 0; i < NUM_SLOTS; i++) {
      targets[i] = nullptr;
      counts[i] = 0;
    }
    countOther = 0;
    MOZ_ASSERT(checkInvariants());
  }

  [[nodiscard]] bool checkInvariants() const {
    // If targets[N] is null, then this slot is not in use and so counts[N]
    // must be zero.  Per comments above about overflow, the implication in the
    // other direction does not hold.
    size_t i;
    for (i = 0; i < NUM_SLOTS; i++) {
      if (targets[i] == nullptr && counts[i] != 0) {
        return false;
      }
    }
    // The targets/counts slots must be filled in in sequence.
    for (i = 0; i < NUM_SLOTS; i++) {
      if (targets[i] == nullptr) {
        break;
      }
    }
    size_t numUsed = i;
    for (/*keepgoing*/; i < NUM_SLOTS; i++) {
      if (targets[i] != nullptr) {
        return false;
      }
    }
    // For the slots in use, the target values must be different
    for (i = 0; i < numUsed; i++) {
      for (size_t j = i + 1; j < numUsed; j++) {
        if (targets[j] == targets[i]) {
          return false;
        }
      }
    }
    // Note we don't say anything about `countOther`.  This gets incremented in
    // the cases when we (1) see a new target when all slots are already in
    // use, or (2) have a cross-instance call.  The effect of (2) is that
    // `countOther` can be non-zero regardless of how many slots are in use.
    return true;
  }

  static size_t offsetOfTarget(size_t n) {
    MOZ_ASSERT(n < NUM_SLOTS);
    return offsetof(CallRefMetrics, targets) + n * sizeof(GCPtr<JSFunction*>);
  }
  static size_t offsetOfCount(size_t n) {
    MOZ_ASSERT(n < NUM_SLOTS);
    return offsetof(CallRefMetrics, counts) + n * sizeof(uint32_t);
  }
  static size_t offsetOfCountOther() {
    return offsetof(CallRefMetrics, countOther);
  }
};

}  // namespace wasm
}  // namespace js

#endif  // wasm_instance_data_h
