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

#include "src/compiler/fast-accessor-assembler.h"

#include "src/base/logging.h"
#include "src/code-stubs.h"  // For CallApiFunctionStub.
#include "src/compiler/graph.h"
#include "src/compiler/linkage.h"
#include "src/compiler/pipeline.h"
#include "src/compiler/raw-machine-assembler.h"
#include "src/compiler/schedule.h"
#include "src/compiler/verifier.h"
#include "src/handles-inl.h"
#include "src/objects.h"  // For FAA::GetInternalField impl.

namespace v8 {
namespace internal {
namespace compiler {

FastAccessorAssembler::FastAccessorAssembler(Isolate* isolate)
    : zone_(),
      assembler_(new RawMachineAssembler(
          isolate, new (zone()) Graph(zone()),
          Linkage::GetJSCallDescriptor(&zone_, false, 1,
                                       CallDescriptor::kNoFlags))),
      state_(kBuilding) {}


FastAccessorAssembler::~FastAccessorAssembler() {}


FastAccessorAssembler::ValueId FastAccessorAssembler::IntegerConstant(
    int const_value) {
  CHECK_EQ(kBuilding, state_);
  return FromRaw(assembler_->NumberConstant(const_value));
}


FastAccessorAssembler::ValueId FastAccessorAssembler::GetReceiver() {
  CHECK_EQ(kBuilding, state_);

  // For JS call descriptor, the receiver is parameter 0. If we use other
  // call descriptors, this may or may not hold. So let's check.
  CHECK(assembler_->call_descriptor()->IsJSFunctionCall());
  return FromRaw(assembler_->Parameter(0));
}


FastAccessorAssembler::ValueId FastAccessorAssembler::LoadInternalField(
    ValueId value, int field_no) {
  CHECK_EQ(kBuilding, state_);
  // Determine the 'value' object's instance type.
  Node* object_map =
      assembler_->Load(MachineType::Pointer(), FromId(value),
                       assembler_->IntPtrConstant(
                           Internals::kHeapObjectMapOffset - kHeapObjectTag));
  Node* instance_type = assembler_->WordAnd(
      assembler_->Load(
          MachineType::Uint16(), object_map,
          assembler_->IntPtrConstant(
              Internals::kMapInstanceTypeAndBitFieldOffset - kHeapObjectTag)),
      assembler_->IntPtrConstant(0xff));

  // Check whether we have a proper JSObject.
  RawMachineLabel is_jsobject, is_not_jsobject, merge;
  assembler_->Branch(
      assembler_->WordEqual(
          instance_type, assembler_->IntPtrConstant(Internals::kJSObjectType)),
      &is_jsobject, &is_not_jsobject);

  // JSObject? Then load the internal field field_no.
  assembler_->Bind(&is_jsobject);
  Node* internal_field = assembler_->Load(
      MachineType::Pointer(), FromId(value),
      assembler_->IntPtrConstant(JSObject::kHeaderSize - kHeapObjectTag +
                                 kPointerSize * field_no));
  assembler_->Goto(&merge);

  // No JSObject? Return undefined.
  // TODO(vogelheim): Check whether this is the appropriate action, or whether
  //                  the method should take a label instead.
  assembler_->Bind(&is_not_jsobject);
  Node* fail_value = assembler_->UndefinedConstant();
  assembler_->Goto(&merge);

  // Return.
  assembler_->Bind(&merge);
  Node* phi = assembler_->Phi(MachineRepresentation::kTagged, internal_field,
                              fail_value);
  return FromRaw(phi);
}


FastAccessorAssembler::ValueId FastAccessorAssembler::LoadValue(ValueId value,
                                                                int offset) {
  CHECK_EQ(kBuilding, state_);
  return FromRaw(assembler_->Load(MachineType::IntPtr(), FromId(value),
                                  assembler_->IntPtrConstant(offset)));
}


FastAccessorAssembler::ValueId FastAccessorAssembler::LoadObject(ValueId value,
                                                                 int offset) {
  CHECK_EQ(kBuilding, state_);
  return FromRaw(
      assembler_->Load(MachineType::AnyTagged(),
                       assembler_->Load(MachineType::Pointer(), FromId(value),
                                        assembler_->IntPtrConstant(offset))));
}


void FastAccessorAssembler::ReturnValue(ValueId value) {
  CHECK_EQ(kBuilding, state_);
  assembler_->Return(FromId(value));
}


void FastAccessorAssembler::CheckFlagSetOrReturnNull(ValueId value, int mask) {
  CHECK_EQ(kBuilding, state_);
  RawMachineLabel pass, fail;
  assembler_->Branch(
      assembler_->Word32Equal(
          assembler_->Word32And(FromId(value), assembler_->Int32Constant(mask)),
          assembler_->Int32Constant(0)),
      &pass, &fail);
  assembler_->Bind(&fail);
  assembler_->Return(assembler_->NullConstant());
  assembler_->Bind(&pass);
}


void FastAccessorAssembler::CheckNotZeroOrReturnNull(ValueId value) {
  CHECK_EQ(kBuilding, state_);
  RawMachineLabel is_null, not_null;
  assembler_->Branch(
      assembler_->IntPtrEqual(FromId(value), assembler_->IntPtrConstant(0)),
      &is_null, &not_null);
  assembler_->Bind(&is_null);
  assembler_->Return(assembler_->NullConstant());
  assembler_->Bind(&not_null);
}


FastAccessorAssembler::LabelId FastAccessorAssembler::MakeLabel() {
  CHECK_EQ(kBuilding, state_);
  RawMachineLabel* label =
      new (zone()->New(sizeof(RawMachineLabel))) RawMachineLabel;
  return FromRaw(label);
}


void FastAccessorAssembler::SetLabel(LabelId label_id) {
  CHECK_EQ(kBuilding, state_);
  assembler_->Bind(FromId(label_id));
}


void FastAccessorAssembler::CheckNotZeroOrJump(ValueId value_id,
                                               LabelId label_id) {
  CHECK_EQ(kBuilding, state_);
  RawMachineLabel pass;
  assembler_->Branch(
      assembler_->IntPtrEqual(FromId(value_id), assembler_->IntPtrConstant(0)),
      &pass, FromId(label_id));
  assembler_->Bind(&pass);
}

FastAccessorAssembler::ValueId FastAccessorAssembler::Call(
    FunctionCallback callback_function, ValueId arg) {
  CHECK_EQ(kBuilding, state_);

  // Create API function stub.
  CallApiFunctionStub stub(assembler_->isolate(), true);

  // Wrap the FunctionCallback in an ExternalReference.
  ApiFunction callback_api_function(FUNCTION_ADDR(callback_function));
  ExternalReference callback(&callback_api_function,
                             ExternalReference::DIRECT_API_CALL,
                             assembler_->isolate());

  // The stub has 5 parameters, and kJSParam (here: 1) parameters to pass
  // through to the callback.
  // See: ApiFunctionDescriptor::BuildCallInterfaceDescriptorFunctionType
  static const int kStackParam = 1;
  Node* args[] = {
      // Stub/register parameters:
      assembler_->Parameter(0),                /* receiver (use accessor's) */
      assembler_->UndefinedConstant(),         /* call_data (undefined) */
      assembler_->NullConstant(),              /* holder (null) */
      assembler_->ExternalConstant(callback),  /* API callback function */
      assembler_->IntPtrConstant(kStackParam), /* # JS arguments */

      // kStackParam stack parameter(s):
      FromId(arg),

      // Context parameter. (See Linkage::GetStubCallDescriptor.)
      assembler_->UndefinedConstant()};
  CHECK_EQ(5 + kStackParam + 1, arraysize(args));

  Node* call = assembler_->CallN(
      Linkage::GetStubCallDescriptor(
          assembler_->isolate(), zone(), stub.GetCallInterfaceDescriptor(),
          kStackParam + stub.GetStackParameterCount(),
          CallDescriptor::kNoFlags),
      assembler_->HeapConstant(stub.GetCode()), args);
  return FromRaw(call);
}

MaybeHandle<Code> FastAccessorAssembler::Build() {
  CHECK_EQ(kBuilding, state_);

  // Cleanup: We no longer need this.
  nodes_.clear();
  labels_.clear();

  // Export the schedule and call the compiler.
  Schedule* schedule = assembler_->Export();
  Code::Flags flags = Code::ComputeFlags(Code::STUB);
  MaybeHandle<Code> code = Pipeline::GenerateCodeForCodeStub(
      assembler_->isolate(), assembler_->call_descriptor(), assembler_->graph(),
      schedule, flags, "FastAccessorAssembler");

  // Update state & return.
  state_ = !code.is_null() ? kBuilt : kError;
  return code;
}


FastAccessorAssembler::ValueId FastAccessorAssembler::FromRaw(Node* node) {
  nodes_.push_back(node);
  ValueId value = {nodes_.size() - 1};
  return value;
}


FastAccessorAssembler::LabelId FastAccessorAssembler::FromRaw(
    RawMachineLabel* label) {
  labels_.push_back(label);
  LabelId label_id = {labels_.size() - 1};
  return label_id;
}


Node* FastAccessorAssembler::FromId(ValueId value) const {
  CHECK_LT(value.value_id, nodes_.size());
  CHECK_NOT_NULL(nodes_.at(value.value_id));
  return nodes_.at(value.value_id);
}


RawMachineLabel* FastAccessorAssembler::FromId(LabelId label) const {
  CHECK_LT(label.label_id, labels_.size());
  CHECK_NOT_NULL(labels_.at(label.label_id));
  return labels_.at(label.label_id);
}


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