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

#ifndef V8_COMPILER_LINKAGE_H_
#define V8_COMPILER_LINKAGE_H_

#include "src/base/flags.h"
#include "src/code-stubs.h"
#include "src/compiler/frame.h"
#include "src/compiler/machine-type.h"
#include "src/compiler/node.h"
#include "src/compiler/operator.h"
#include "src/zone.h"

namespace v8 {
namespace internal {
namespace compiler {

// Describes the location for a parameter or a return value to a call.
class LinkageLocation {
 public:
  explicit LinkageLocation(int location) : location_(location) {}

  static const int16_t ANY_REGISTER = 32767;

  static LinkageLocation AnyRegister() { return LinkageLocation(ANY_REGISTER); }

 private:
  friend class CallDescriptor;
  friend class OperandGenerator;
  int16_t location_;  // >= 0 implies register, otherwise stack slot.
};

typedef Signature<LinkageLocation> LocationSignature;

// Describes a call to various parts of the compiler. Every call has the notion
// of a "target", which is the first input to the call.
class CallDescriptor FINAL : public ZoneObject {
 public:
  // Describes the kind of this call, which determines the target.
  enum Kind {
    kCallCodeObject,  // target is a Code object
    kCallJSFunction,  // target is a JSFunction object
    kCallAddress      // target is a machine pointer
  };

  enum Flag {
    // TODO(jarin) kLazyDeoptimization and kNeedsFrameState should be unified.
    kNoFlags = 0u,
    kNeedsFrameState = 1u << 0,
    kPatchableCallSite = 1u << 1,
    kNeedsNopAfterCall = 1u << 2,
    kPatchableCallSiteWithNop = kPatchableCallSite | kNeedsNopAfterCall
  };
  typedef base::Flags<Flag> Flags;

  CallDescriptor(Kind kind, MachineType target_type, LinkageLocation target_loc,
                 MachineSignature* machine_sig, LocationSignature* location_sig,
                 size_t js_param_count, Operator::Properties properties,
                 RegList callee_saved_registers, Flags flags,
                 const char* debug_name = "")
      : kind_(kind),
        target_type_(target_type),
        target_loc_(target_loc),
        machine_sig_(machine_sig),
        location_sig_(location_sig),
        js_param_count_(js_param_count),
        properties_(properties),
        callee_saved_registers_(callee_saved_registers),
        flags_(flags),
        debug_name_(debug_name) {
    DCHECK(machine_sig->return_count() == location_sig->return_count());
    DCHECK(machine_sig->parameter_count() == location_sig->parameter_count());
  }

  // Returns the kind of this call.
  Kind kind() const { return kind_; }

  // Returns {true} if this descriptor is a call to a JSFunction.
  bool IsJSFunctionCall() const { return kind_ == kCallJSFunction; }

  // The number of return values from this call.
  size_t ReturnCount() const { return machine_sig_->return_count(); }

  // The number of JavaScript parameters to this call, including the receiver
  // object.
  size_t JSParameterCount() const { return js_param_count_; }

  // The total number of inputs to this call, which includes the target,
  // receiver, context, etc.
  // TODO(titzer): this should input the framestate input too.
  size_t InputCount() const { return 1 + machine_sig_->parameter_count(); }

  size_t FrameStateCount() const { return NeedsFrameState() ? 1 : 0; }

  Flags flags() const { return flags_; }

  bool NeedsFrameState() const { return flags() & kNeedsFrameState; }

  LinkageLocation GetReturnLocation(size_t index) const {
    return location_sig_->GetReturn(index);
  }

  LinkageLocation GetInputLocation(size_t index) const {
    if (index == 0) return target_loc_;
    return location_sig_->GetParam(index - 1);
  }

  const MachineSignature* GetMachineSignature() const { return machine_sig_; }

  MachineType GetReturnType(size_t index) const {
    return machine_sig_->GetReturn(index);
  }

  MachineType GetInputType(size_t index) const {
    if (index == 0) return target_type_;
    return machine_sig_->GetParam(index - 1);
  }

  // Operator properties describe how this call can be optimized, if at all.
  Operator::Properties properties() const { return properties_; }

  // Get the callee-saved registers, if any, across this call.
  RegList CalleeSavedRegisters() const { return callee_saved_registers_; }

  const char* debug_name() const { return debug_name_; }

 private:
  friend class Linkage;

  Kind kind_;
  MachineType target_type_;
  LinkageLocation target_loc_;
  MachineSignature* machine_sig_;
  LocationSignature* location_sig_;
  size_t js_param_count_;
  Operator::Properties properties_;
  RegList callee_saved_registers_;
  Flags flags_;
  const char* debug_name_;
};

DEFINE_OPERATORS_FOR_FLAGS(CallDescriptor::Flags)

OStream& operator<<(OStream& os, const CallDescriptor& d);
OStream& operator<<(OStream& os, const CallDescriptor::Kind& k);

// Defines the linkage for a compilation, including the calling conventions
// for incoming parameters and return value(s) as well as the outgoing calling
// convention for any kind of call. Linkage is generally architecture-specific.
//
// Can be used to translate {arg_index} (i.e. index of the call node input) as
// well as {param_index} (i.e. as stored in parameter nodes) into an operator
// representing the architecture-specific location. The following call node
// layouts are supported (where {n} is the number value inputs):
//
//                  #0          #1     #2     #3     [...]             #n
// Call[CodeStub]   code,       arg 1, arg 2, arg 3, [...],            context
// Call[JSFunction] function,   rcvr,  arg 1, arg 2, [...],            context
// Call[Runtime]    CEntryStub, arg 1, arg 2, arg 3, [...], fun, #arg, context
class Linkage : public ZoneObject {
 public:
  explicit Linkage(CompilationInfo* info);
  explicit Linkage(CompilationInfo* info, CallDescriptor* incoming)
      : info_(info), incoming_(incoming) {}

  // The call descriptor for this compilation unit describes the locations
  // of incoming parameters and the outgoing return value(s).
  CallDescriptor* GetIncomingDescriptor() { return incoming_; }
  CallDescriptor* GetJSCallDescriptor(int parameter_count);
  static CallDescriptor* GetJSCallDescriptor(int parameter_count, Zone* zone);
  CallDescriptor* GetRuntimeCallDescriptor(Runtime::FunctionId function,
                                           int parameter_count,
                                           Operator::Properties properties);
  static CallDescriptor* GetRuntimeCallDescriptor(
      Runtime::FunctionId function, int parameter_count,
      Operator::Properties properties, Zone* zone);

  CallDescriptor* GetStubCallDescriptor(
      CallInterfaceDescriptor descriptor, int stack_parameter_count = 0,
      CallDescriptor::Flags flags = CallDescriptor::kNoFlags);
  static CallDescriptor* GetStubCallDescriptor(
      CallInterfaceDescriptor descriptor, int stack_parameter_count,
      CallDescriptor::Flags flags, Zone* zone);

  // Creates a call descriptor for simplified C calls that is appropriate
  // for the host platform. This simplified calling convention only supports
  // integers and pointers of one word size each, i.e. no floating point,
  // structs, pointers to members, etc.
  static CallDescriptor* GetSimplifiedCDescriptor(Zone* zone,
                                                  MachineSignature* sig);

  // Get the location of an (incoming) parameter to this function.
  LinkageLocation GetParameterLocation(int index) {
    return incoming_->GetInputLocation(index + 1);  // + 1 to skip target.
  }

  // Get the machine type of an (incoming) parameter to this function.
  MachineType GetParameterType(int index) {
    return incoming_->GetInputType(index + 1);  // + 1 to skip target.
  }

  // Get the location where this function should place its return value.
  LinkageLocation GetReturnLocation() {
    return incoming_->GetReturnLocation(0);
  }

  // Get the machine type of this function's return value.
  MachineType GetReturnType() { return incoming_->GetReturnType(0); }

  // Get the frame offset for a given spill slot. The location depends on the
  // calling convention and the specific frame layout, and may thus be
  // architecture-specific. Negative spill slots indicate arguments on the
  // caller's frame. The {extra} parameter indicates an additional offset from
  // the frame offset, e.g. to index into part of a double slot.
  FrameOffset GetFrameOffset(int spill_slot, Frame* frame, int extra = 0);

  CompilationInfo* info() const { return info_; }

  static bool NeedsFrameState(Runtime::FunctionId function);

 private:
  CompilationInfo* info_;
  CallDescriptor* incoming_;
};

}  // namespace compiler
}  // namespace internal
}  // namespace v8

#endif  // V8_COMPILER_LINKAGE_H_
