// Copyright 2018 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.

#ifndef V8_WASM_FUNCTION_COMPILER_H_
#define V8_WASM_FUNCTION_COMPILER_H_

#if !V8_ENABLE_WEBASSEMBLY
#error This header should only be included if WebAssembly is enabled.
#endif  // !V8_ENABLE_WEBASSEMBLY

#include <memory>

#include "src/codegen/assembler.h"
#include "src/codegen/code-desc.h"
#include "src/codegen/compiler.h"
#include "src/wasm/baseline/liftoff-bailout-reasons.h"
#include "src/wasm/compilation-environment.h"
#include "src/wasm/function-body-decoder.h"
#include "src/wasm/wasm-code-manager.h"
#include "src/wasm/wasm-deopt-data.h"
#include "src/wasm/wasm-limits.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-tier.h"

namespace v8::internal::wasm {

class NativeModule;
struct WasmFunction;

using Validation = base::StrongAlias<struct ValidationFlagTag, bool>;
constexpr Validation kAlreadyValidated{false};
constexpr Validation kMustValidate{true};

// Stores assumptions that a Wasm compilation job made while executing,
// so they can be checked for continued validity when the job finishes.
class AssumptionsJournal {
 public:
  AssumptionsJournal() = default;

  void RecordAssumption(uint32_t func_index, WellKnownImport status) {
    imports_.push_back(std::make_pair(func_index, status));
  }

  const std::vector<std::pair<uint32_t, WellKnownImport>>& import_statuses() {
    return imports_;
  }

  bool empty() const { return imports_.empty(); }

 private:
  // This is not particularly efficient, but it's probably good enough.
  // For most compilations, this won't hold any entries. If it does
  // hold entries, their number is expected to be small, because most
  // functions don't call many imports, and many imports won't be
  // specially recognized.
  std::vector<std::pair<uint32_t, WellKnownImport>> imports_;
};

struct WasmCompilationResult {
 public:
  MOVE_ONLY_WITH_DEFAULT_CONSTRUCTORS(WasmCompilationResult);

  enum Kind : int8_t {
    kFunction,
    kWasmToJsWrapper,
    kStackEntryWrapper,
#if V8_ENABLE_DRUMBRAKE
    kInterpreterEntry,
#endif  // V8_ENABLE_DRUMBRAKE
  };

  bool succeeded() const { return code_desc.buffer != nullptr; }
  bool failed() const { return !succeeded(); }
  explicit operator bool() const { return succeeded(); }

  CodeDesc code_desc;
  std::unique_ptr<AssemblerBuffer> instr_buffer;
  uint32_t frame_slot_count = 0;
  uint32_t ool_spill_count = 0;
  uint32_t tagged_parameter_slots = 0;
  base::OwnedVector<uint8_t> source_positions;
  base::OwnedVector<uint8_t> inlining_positions;
  base::OwnedVector<uint8_t> trapping_instructions_data;
  base::OwnedVector<uint8_t> deopt_data;
  base::OwnedVector<uint8_t> effect_handlers;
  std::unique_ptr<AssumptionsJournal> assumptions;
  std::unique_ptr<LiftoffFrameDescriptionForDeopt> liftoff_frame_descriptions;
  int func_index = kAnonymousFuncIndex;
  ExecutionTier result_tier = ExecutionTier::kNone;
  Kind kind = kFunction;
  ForDebugging for_debugging = kNotForDebugging;
  bool frame_has_feedback_slot = false;
  LiftoffBailoutReason bailout_reason = kNoReason;
};

class V8_EXPORT_PRIVATE WasmCompilationUnit final {
 public:
  WasmCompilationUnit(int index, ExecutionTier tier, ForDebugging for_debugging,
                      Validation validation)
      : func_index_(index),
        tier_(tier),
        for_debugging_(for_debugging),
        validation_(validation) {
    DCHECK_IMPLIES(for_debugging != ForDebugging::kNotForDebugging,
                   tier_ == ExecutionTier::kLiftoff);
  }

  WasmCompilationResult ExecuteCompilation(CompilationEnv*,
                                           const WireBytesStorage*,
                                           DelayedCounterUpdates*,
                                           WasmDetectedFeatures*);

  ExecutionTier tier() const { return tier_; }
  ForDebugging for_debugging() const { return for_debugging_; }
  int func_index() const { return func_index_; }
  Validation validation() const { return validation_; }

  static void CompileWasmFunction(NativeModule*, WasmDetectedFeatures*,
                                  const WasmFunction*, ExecutionTier);

 private:
  int func_index_;
  ExecutionTier tier_;
  ForDebugging for_debugging_;
  Validation validation_;
};

// {WasmCompilationUnit} should be trivially copyable and small enough so we can
// efficiently pass it by value.
ASSERT_TRIVIALLY_COPYABLE(WasmCompilationUnit);
static_assert(sizeof(WasmCompilationUnit) <= 2 * kSystemPointerSize);

// TODO(jkummerow): Most of this could move into the .cc file.
class V8_EXPORT_PRIVATE JSToWasmWrapperCompilationUnit final {
 public:
  JSToWasmWrapperCompilationUnit(Isolate* isolate, const CanonicalSig* sig);
  ~JSToWasmWrapperCompilationUnit();

  // Allow move construction and assignment, for putting units in a std::vector.
  JSToWasmWrapperCompilationUnit(JSToWasmWrapperCompilationUnit&&)
      V8_NOEXCEPT = default;
  JSToWasmWrapperCompilationUnit& operator=(JSToWasmWrapperCompilationUnit&&)
      V8_NOEXCEPT = default;

  void Execute();
  DirectHandle<Code> Finalize();

  // Run a compilation unit synchronously.
  static DirectHandle<Code> CompileJSToWasmWrapper(Isolate* isolate,
                                                   const CanonicalSig* sig);

 private:
  // Wrapper compilation is bound to an isolate. Concurrent accesses to the
  // isolate (during the "Execute" phase) must be audited carefully, i.e. we
  // should only access immutable information (like the root table). The isolate
  // is guaranteed to be alive when this unit executes.
  Isolate* isolate_;
  const CanonicalSig* sig_;
  std::unique_ptr<OptimizedCompilationJob> job_;
};

inline bool CanUseGenericJsToWasmWrapper(const CanonicalSig* sig) {
#if (V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_ARM64 || V8_TARGET_ARCH_IA32 ||  \
     V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_S390X || V8_TARGET_ARCH_PPC64 || \
     V8_TARGET_ARCH_LOONG64 || V8_TARGET_ARCH_RISCV64)
  return v8_flags.wasm_generic_wrapper && IsJSCompatibleSignature(sig);
#else
  return false;
#endif
}

}  // namespace v8::internal::wasm

#endif  // V8_WASM_FUNCTION_COMPILER_H_
