/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * 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 ART_COMPILER_UTILS_ARM64_MANAGED_REGISTER_ARM64_H_
#define ART_COMPILER_UTILS_ARM64_MANAGED_REGISTER_ARM64_H_

#include <android-base/logging.h>

#include "arch/arm64/registers_arm64.h"
#include "utils/managed_register.h"

namespace art {
namespace arm64 {

const int kNumberOfXRegIds = kNumberOfXRegisters;
const int kNumberOfWRegIds = kNumberOfWRegisters;
const int kNumberOfDRegIds = kNumberOfDRegisters;
const int kNumberOfSRegIds = kNumberOfSRegisters;

const int kNumberOfRegIds = kNumberOfXRegIds + kNumberOfWRegIds +
  kNumberOfDRegIds + kNumberOfSRegIds;

// Register ids map:
//  [0..X[  core registers 64bit (enum XRegister)
//  [X..W[  core registers 32bit (enum WRegister)
//  [W..D[  double precision VFP registers (enum DRegister)
//  [D..S[  single precision VFP registers (enum SRegister)
//
// where:
//  X = kNumberOfXRegIds
//  W = X + kNumberOfWRegIds
//  D = W + kNumberOfDRegIds
//  S = D + kNumberOfSRegIds
//
// An instance of class 'ManagedRegister' represents a single Arm64
// register. A register can be one of the following:
//  * core register 64bit context (enum XRegister)
//  * core register 32bit context (enum WRegister)
//  * VFP double precision register (enum DRegister)
//  * VFP single precision register (enum SRegister)
//
// There is a one to one mapping between ManagedRegister and register id.

class Arm64ManagedRegister : public ManagedRegister {
 public:
  constexpr XRegister AsXRegister() const {
    CHECK(IsXRegister());
    return static_cast<XRegister>(id_);
  }

  constexpr WRegister AsWRegister() const {
    CHECK(IsWRegister());
    return static_cast<WRegister>(id_ - kNumberOfXRegIds);
  }

  constexpr DRegister AsDRegister() const {
    CHECK(IsDRegister());
    return static_cast<DRegister>(id_ - kNumberOfXRegIds - kNumberOfWRegIds);
  }

  constexpr SRegister AsSRegister() const {
    CHECK(IsSRegister());
    return static_cast<SRegister>(id_ - kNumberOfXRegIds - kNumberOfWRegIds -
                                  kNumberOfDRegIds);
  }

  constexpr WRegister AsOverlappingWRegister() const {
    CHECK(IsValidManagedRegister());
    if (IsZeroRegister()) return WZR;
    return static_cast<WRegister>(AsXRegister());
  }

  constexpr XRegister AsOverlappingXRegister() const {
    CHECK(IsValidManagedRegister());
    return static_cast<XRegister>(AsWRegister());
  }

  constexpr SRegister AsOverlappingSRegister() const {
    CHECK(IsValidManagedRegister());
    return static_cast<SRegister>(AsDRegister());
  }

  constexpr DRegister AsOverlappingDRegister() const {
    CHECK(IsValidManagedRegister());
    return static_cast<DRegister>(AsSRegister());
  }

  constexpr bool IsXRegister() const {
    CHECK(IsValidManagedRegister());
    return (0 <= id_) && (id_ < kNumberOfXRegIds);
  }

  constexpr bool IsWRegister() const {
    CHECK(IsValidManagedRegister());
    const int test = id_ - kNumberOfXRegIds;
    return (0 <= test) && (test < kNumberOfWRegIds);
  }

  constexpr bool IsDRegister() const {
    CHECK(IsValidManagedRegister());
    const int test = id_ - (kNumberOfXRegIds + kNumberOfWRegIds);
    return (0 <= test) && (test < kNumberOfDRegIds);
  }

  constexpr bool IsSRegister() const {
    CHECK(IsValidManagedRegister());
    const int test = id_ - (kNumberOfXRegIds + kNumberOfWRegIds + kNumberOfDRegIds);
    return (0 <= test) && (test < kNumberOfSRegIds);
  }

  constexpr bool IsGPRegister() const {
    return IsXRegister() || IsWRegister();
  }

  constexpr bool IsFPRegister() const {
    return IsDRegister() || IsSRegister();
  }

  constexpr bool IsSameType(Arm64ManagedRegister test) const {
    CHECK(IsValidManagedRegister() && test.IsValidManagedRegister());
    return
      (IsXRegister() && test.IsXRegister()) ||
      (IsWRegister() && test.IsWRegister()) ||
      (IsDRegister() && test.IsDRegister()) ||
      (IsSRegister() && test.IsSRegister());
  }

  // Returns true if the two managed-registers ('this' and 'other') overlap.
  // Either managed-register may be the NoRegister. If both are the NoRegister
  // then false is returned.
  bool Overlaps(const Arm64ManagedRegister& other) const;

  void Print(std::ostream& os) const;

  static constexpr Arm64ManagedRegister FromXRegister(XRegister r) {
    CHECK_NE(r, kNoRegister);
    return FromRegId(r);
  }

  static constexpr Arm64ManagedRegister FromWRegister(WRegister r) {
    CHECK_NE(r, kNoWRegister);
    return FromRegId(r + kNumberOfXRegIds);
  }

  static constexpr Arm64ManagedRegister FromDRegister(DRegister r) {
    CHECK_NE(r, kNoDRegister);
    return FromRegId(r + (kNumberOfXRegIds + kNumberOfWRegIds));
  }

  static constexpr Arm64ManagedRegister FromSRegister(SRegister r) {
    CHECK_NE(r, kNoSRegister);
    return FromRegId(r + (kNumberOfXRegIds + kNumberOfWRegIds +
                          kNumberOfDRegIds));
  }

  // Returns the X register overlapping W register r.
  static constexpr Arm64ManagedRegister FromWRegisterX(WRegister r) {
    CHECK_NE(r, kNoWRegister);
    return FromRegId(r);
  }

  // Return the D register overlapping S register r.
  static constexpr Arm64ManagedRegister FromSRegisterD(SRegister r) {
    CHECK_NE(r, kNoSRegister);
    return FromRegId(r + (kNumberOfXRegIds + kNumberOfWRegIds));
  }

 private:
  constexpr bool IsValidManagedRegister() const {
    return (0 <= id_) && (id_ < kNumberOfRegIds);
  }

  constexpr bool IsStackPointer() const {
    return IsXRegister() && (id_ == SP);
  }

  constexpr bool IsZeroRegister() const {
    return IsXRegister() && (id_ == XZR);
  }

  constexpr int RegId() const {
    CHECK(!IsNoRegister());
    return id_;
  }

  int RegNo() const;
  int RegIdLow() const;
  int RegIdHigh() const;

  friend class ManagedRegister;

  explicit constexpr Arm64ManagedRegister(int reg_id) : ManagedRegister(reg_id) {}

  static constexpr Arm64ManagedRegister FromRegId(int reg_id) {
    Arm64ManagedRegister reg(reg_id);
    CHECK(reg.IsValidManagedRegister());
    return reg;
  }
};

std::ostream& operator<<(std::ostream& os, const Arm64ManagedRegister& reg);

}  // namespace arm64

constexpr arm64::Arm64ManagedRegister ManagedRegister::AsArm64() const {
  arm64::Arm64ManagedRegister reg(id_);
  CHECK(reg.IsNoRegister() || reg.IsValidManagedRegister());
  return reg;
}

}  // namespace art

#endif  // ART_COMPILER_UTILS_ARM64_MANAGED_REGISTER_ARM64_H_
