//===-- Scalar.cpp --------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "lldb/Utility/Scalar.h"
#include "lldb/Utility/DataBufferHeap.h"
#include "lldb/Utility/DataExtractor.h"
#include "lldb/Utility/Endian.h"
#include "lldb/Utility/Status.h"
#include "lldb/Utility/Stream.h"
#include "lldb/Utility/StreamString.h"
#include "lldb/lldb-types.h"
#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"

#include <cinttypes>
#include <cstdio>

using namespace lldb;
using namespace lldb_private;

using llvm::APFloat;
using llvm::APInt;
using llvm::APSInt;

Scalar::PromotionKey Scalar::GetPromoKey() const {
  switch (m_type) {
  case e_void:
    return PromotionKey{e_void, 0, false};
  case e_int:
    return PromotionKey{e_int, m_integer.getBitWidth(), m_integer.isUnsigned()};
  case e_float:
    return GetFloatPromoKey(m_float.getSemantics());
  }
  llvm_unreachable("Unhandled category!");
}

Scalar::PromotionKey Scalar::GetFloatPromoKey(const llvm::fltSemantics &sem) {
  static const llvm::fltSemantics *const order[] = {
      &APFloat::IEEEsingle(), &APFloat::IEEEdouble(),
      &APFloat::x87DoubleExtended()};
  for (const auto &entry : llvm::enumerate(order)) {
    if (entry.value() == &sem)
      return PromotionKey{e_float, entry.index(), false};
  }
  llvm_unreachable("Unsupported semantics!");
}

// Promote to max type currently follows the ANSI C rule for type promotion in
// expressions.
Scalar::Type Scalar::PromoteToMaxType(Scalar &lhs, Scalar &rhs) {
  const auto &Promote = [](Scalar &a, const Scalar &b) {
    switch (b.GetType()) {
    case e_void:
      break;
    case e_int:
      a.IntegralPromote(b.m_integer.getBitWidth(), b.m_integer.isSigned());
      break;
    case e_float:
      a.FloatPromote(b.m_float.getSemantics());
    }
  };

  PromotionKey lhs_key = lhs.GetPromoKey();
  PromotionKey rhs_key = rhs.GetPromoKey();

  if (lhs_key > rhs_key)
    Promote(rhs, lhs);
  else if (rhs_key > lhs_key)
    Promote(lhs, rhs);

  // Make sure our type promotion worked as expected
  if (lhs.GetPromoKey() == rhs.GetPromoKey())
    return lhs.GetType(); // Return the resulting type

  // Return the void type (zero) if we fail to promote either of the values.
  return Scalar::e_void;
}

bool Scalar::GetData(DataExtractor &data, size_t limit_byte_size) const {
  size_t byte_size = GetByteSize();
  if (byte_size == 0) {
    data.Clear();
    return false;
  }
  auto buffer_up = std::make_unique<DataBufferHeap>(byte_size, 0);
  GetBytes(buffer_up->GetData());
  lldb::offset_t offset = 0;

  if (limit_byte_size < byte_size) {
    if (endian::InlHostByteOrder() == eByteOrderLittle) {
      // On little endian systems if we want fewer bytes from the current
      // type we just specify fewer bytes since the LSByte is first...
      byte_size = limit_byte_size;
    } else if (endian::InlHostByteOrder() == eByteOrderBig) {
      // On big endian systems if we want fewer bytes from the current type
      // have to advance our initial byte pointer and trim down the number of
      // bytes since the MSByte is first
      offset = byte_size - limit_byte_size;
      byte_size = limit_byte_size;
    }
  }

  data.SetData(std::move(buffer_up), offset, byte_size);
  data.SetByteOrder(endian::InlHostByteOrder());
  return true;
}

void Scalar::GetBytes(llvm::MutableArrayRef<uint8_t> storage) const {
  assert(storage.size() >= GetByteSize());

  const auto &store = [&](const llvm::APInt &val) {
    StoreIntToMemory(val, storage.data(), (val.getBitWidth() + 7) / 8);
  };
  switch (m_type) {
  case e_void:
    break;
  case e_int:
    store(m_integer);
    break;
  case e_float:
    store(m_float.bitcastToAPInt());
    break;
  }
}

size_t Scalar::GetByteSize() const {
  switch (m_type) {
  case e_void:
    break;
  case e_int:
    return (m_integer.getBitWidth() + 7) / 8;
  case e_float:
    return (m_float.bitcastToAPInt().getBitWidth() + 7) / 8;
  }
  return 0;
}

bool Scalar::IsZero() const {
  switch (m_type) {
  case e_void:
    break;
  case e_int:
    return m_integer.isZero();
  case e_float:
    return m_float.isZero();
  }
  return false;
}

void Scalar::GetValue(Stream &s, bool show_type) const {
  if (show_type)
    s.Printf("(%s) ", GetTypeAsCString());

  switch (m_type) {
  case e_void:
    break;
  case e_int:
    s.PutCString(llvm::toString(m_integer, 10));
    break;
  case e_float:
    llvm::SmallString<24> string;
    m_float.toString(string);
    s.PutCString(string);
    break;
  }
}

void Scalar::TruncOrExtendTo(uint16_t bits, bool sign) {
  m_integer.setIsSigned(sign);
  m_integer = m_integer.extOrTrunc(bits);
}

bool Scalar::IntegralPromote(uint16_t bits, bool sign) {
  switch (m_type) {
  case e_void:
  case e_float:
    break;
  case e_int:
    if (GetPromoKey() > PromotionKey(e_int, bits, !sign))
      break;
    m_integer = m_integer.extOrTrunc(bits);
    m_integer.setIsSigned(sign);
    return true;
  }
  return false;
}

bool Scalar::FloatPromote(const llvm::fltSemantics &semantics) {
  bool success = false;
  switch (m_type) {
  case e_void:
    break;
  case e_int:
    m_float = llvm::APFloat(semantics);
    m_float.convertFromAPInt(m_integer, m_integer.isSigned(),
                             llvm::APFloat::rmNearestTiesToEven);
    success = true;
    break;
  case e_float:
    if (GetFloatPromoKey(semantics) < GetFloatPromoKey(m_float.getSemantics()))
      break;
    bool ignore;
    success = true;
    m_float.convert(semantics, llvm::APFloat::rmNearestTiesToEven, &ignore);
  }

  if (success)
    m_type = e_float;
  return success;
}

const char *Scalar::GetValueTypeAsCString(Scalar::Type type) {
  switch (type) {
  case e_void:
    return "void";
  case e_int:
    return "int";
  case e_float:
    return "float";
  }
  return "???";
}

bool Scalar::IsSigned() const {
  switch (m_type) {
  case e_void:
    return false;
  case e_int:
    return m_integer.isSigned();
  case e_float:
    return true;
  }
  llvm_unreachable("Unrecognized type!");
}

bool Scalar::MakeSigned() {
  bool success = false;

  switch (m_type) {
  case e_void:
    break;
  case e_int:
    m_integer.setIsSigned(true);
    success = true;
    break;
  case e_float:
    success = true;
    break;
  }

  return success;
}

bool Scalar::MakeUnsigned() {
  bool success = false;

  switch (m_type) {
  case e_void:
    break;
  case e_int:
    m_integer.setIsUnsigned(true);
    success = true;
    break;
  case e_float:
    success = true;
    break;
  }

  return success;
}

static llvm::APInt ToAPInt(const llvm::APFloat &f, unsigned bits,
                           bool is_unsigned) {
  llvm::APSInt result(bits, is_unsigned);
  bool isExact;
  f.convertToInteger(result, llvm::APFloat::rmTowardZero, &isExact);
  return std::move(result);
}

template <typename T> T Scalar::GetAs(T fail_value) const {
  switch (m_type) {
  case e_void:
    break;
  case e_int: {
    APSInt ext = m_integer.extOrTrunc(sizeof(T) * 8);
    if (ext.isSigned())
      return ext.getSExtValue();
    return ext.getZExtValue();
  }
  case e_float:
    return ToAPInt(m_float, sizeof(T) * 8, std::is_unsigned<T>::value)
        .getSExtValue();
  }
  return fail_value;
}

signed char Scalar::SChar(signed char fail_value) const {
  return GetAs<signed char>(fail_value);
}

unsigned char Scalar::UChar(unsigned char fail_value) const {
  return GetAs<unsigned char>(fail_value);
}

short Scalar::SShort(short fail_value) const {
  return GetAs<short>(fail_value);
}

unsigned short Scalar::UShort(unsigned short fail_value) const {
  return GetAs<unsigned short>(fail_value);
}

int Scalar::SInt(int fail_value) const { return GetAs<int>(fail_value); }

unsigned int Scalar::UInt(unsigned int fail_value) const {
  return GetAs<unsigned int>(fail_value);
}

long Scalar::SLong(long fail_value) const { return GetAs<long>(fail_value); }

unsigned long Scalar::ULong(unsigned long fail_value) const {
  return GetAs<unsigned long>(fail_value);
}

long long Scalar::SLongLong(long long fail_value) const {
  return GetAs<long long>(fail_value);
}

unsigned long long Scalar::ULongLong(unsigned long long fail_value) const {
  return GetAs<unsigned long long>(fail_value);
}

llvm::APInt Scalar::SInt128(const llvm::APInt &fail_value) const {
  switch (m_type) {
  case e_void:
    break;
  case e_int:
    return m_integer;
  case e_float:
    return ToAPInt(m_float, 128, /*is_unsigned=*/false);
  }
  return fail_value;
}

llvm::APInt Scalar::UInt128(const llvm::APInt &fail_value) const {
  switch (m_type) {
  case e_void:
    break;
  case e_int:
    return m_integer;
  case e_float:
    return ToAPInt(m_float, 128, /*is_unsigned=*/true);
  }
  return fail_value;
}

float Scalar::Float(float fail_value) const {
  switch (m_type) {
  case e_void:
    break;
  case e_int:
    if (m_integer.isSigned())
      return llvm::APIntOps::RoundSignedAPIntToFloat(m_integer);
    return llvm::APIntOps::RoundAPIntToFloat(m_integer);

  case e_float: {
    APFloat result = m_float;
    bool losesInfo;
    result.convert(APFloat::IEEEsingle(), APFloat::rmNearestTiesToEven,
                   &losesInfo);
    return result.convertToFloat();
  }
  }
  return fail_value;
}

double Scalar::Double(double fail_value) const {
  switch (m_type) {
  case e_void:
    break;
  case e_int:
    if (m_integer.isSigned())
      return llvm::APIntOps::RoundSignedAPIntToDouble(m_integer);
    return llvm::APIntOps::RoundAPIntToDouble(m_integer);

  case e_float: {
    APFloat result = m_float;
    bool losesInfo;
    result.convert(APFloat::IEEEdouble(), APFloat::rmNearestTiesToEven,
                   &losesInfo);
    return result.convertToDouble();
  }
  }
  return fail_value;
}

long double Scalar::LongDouble(long double fail_value) const {
  /// No way to get more precision at the moment.
  return static_cast<long double>(Double(fail_value));
}

Scalar &Scalar::operator+=(Scalar rhs) {
  Scalar copy = *this;
  if ((m_type = PromoteToMaxType(copy, rhs)) != Scalar::e_void) {
    switch (m_type) {
    case e_void:
      break;
    case e_int:
      m_integer = copy.m_integer + rhs.m_integer;
      break;

    case e_float:
      m_float = copy.m_float + rhs.m_float;
      break;
    }
  }
  return *this;
}

Scalar &Scalar::operator<<=(const Scalar &rhs) {
  if (m_type == e_int && rhs.m_type == e_int)
    static_cast<APInt &>(m_integer) <<= rhs.m_integer;
  else
    m_type = e_void;
  return *this;
}

bool Scalar::ShiftRightLogical(const Scalar &rhs) {
  if (m_type == e_int && rhs.m_type == e_int) {
    m_integer = m_integer.lshr(rhs.m_integer);
    return true;
  }
  m_type = e_void;
  return false;
}

Scalar &Scalar::operator>>=(const Scalar &rhs) {
  switch (m_type) {
  case e_void:
  case e_float:
    m_type = e_void;
    break;

  case e_int:
    switch (rhs.m_type) {
    case e_void:
    case e_float:
      m_type = e_void;
      break;
    case e_int:
      m_integer = m_integer.ashr(rhs.m_integer);
      break;
    }
    break;
  }
  return *this;
}

Scalar &Scalar::operator&=(const Scalar &rhs) {
  if (m_type == e_int && rhs.m_type == e_int)
    m_integer &= rhs.m_integer;
  else
    m_type = e_void;
  return *this;
}

bool Scalar::AbsoluteValue() {
  switch (m_type) {
  case e_void:
    break;

  case e_int:
    if (m_integer.isNegative())
      m_integer = -m_integer;
    return true;

  case e_float:
    m_float.clearSign();
    return true;
  }
  return false;
}

bool Scalar::UnaryNegate() {
  switch (m_type) {
  case e_void:
    break;
  case e_int:
    m_integer = -m_integer;
    return true;
  case e_float:
    m_float.changeSign();
    return true;
  }
  return false;
}

bool Scalar::OnesComplement() {
  if (m_type == e_int) {
    m_integer = ~m_integer;
    return true;
  }

  return false;
}

const Scalar lldb_private::operator+(const Scalar &lhs, const Scalar &rhs) {
  Scalar result = lhs;
  result += rhs;
  return result;
}

const Scalar lldb_private::operator-(Scalar lhs, Scalar rhs) {
  Scalar result;
  if ((result.m_type = Scalar::PromoteToMaxType(lhs, rhs)) != Scalar::e_void) {
    switch (result.m_type) {
    case Scalar::e_void:
      break;
    case Scalar::e_int:
      result.m_integer = lhs.m_integer - rhs.m_integer;
      break;
    case Scalar::e_float:
      result.m_float = lhs.m_float - rhs.m_float;
      break;
    }
  }
  return result;
}

const Scalar lldb_private::operator/(Scalar lhs, Scalar rhs) {
  Scalar result;
  if ((result.m_type = Scalar::PromoteToMaxType(lhs, rhs)) != Scalar::e_void &&
      !rhs.IsZero()) {
    switch (result.m_type) {
    case Scalar::e_void:
      break;
    case Scalar::e_int:
      result.m_integer = lhs.m_integer / rhs.m_integer;
      return result;
    case Scalar::e_float:
      result.m_float = lhs.m_float / rhs.m_float;
      return result;
    }
  }
  // For division only, the only way it should make it here is if a promotion
  // failed, or if we are trying to do a divide by zero.
  result.m_type = Scalar::e_void;
  return result;
}

const Scalar lldb_private::operator*(Scalar lhs, Scalar rhs) {
  Scalar result;
  if ((result.m_type = Scalar::PromoteToMaxType(lhs, rhs)) != Scalar::e_void) {
    switch (result.m_type) {
    case Scalar::e_void:
      break;
    case Scalar::e_int:
      result.m_integer = lhs.m_integer * rhs.m_integer;
      break;
    case Scalar::e_float:
      result.m_float = lhs.m_float * rhs.m_float;
      break;
    }
  }
  return result;
}

const Scalar lldb_private::operator&(Scalar lhs, Scalar rhs) {
  Scalar result;
  if ((result.m_type = Scalar::PromoteToMaxType(lhs, rhs)) != Scalar::e_void) {
    if (result.m_type == Scalar::e_int)
      result.m_integer = lhs.m_integer & rhs.m_integer;
    else
      result.m_type = Scalar::e_void;
  }
  return result;
}

const Scalar lldb_private::operator|(Scalar lhs, Scalar rhs) {
  Scalar result;
  if ((result.m_type = Scalar::PromoteToMaxType(lhs, rhs)) != Scalar::e_void) {
    if (result.m_type == Scalar::e_int)
      result.m_integer = lhs.m_integer | rhs.m_integer;
    else
      result.m_type = Scalar::e_void;
  }
  return result;
}

const Scalar lldb_private::operator%(Scalar lhs, Scalar rhs) {
  Scalar result;
  if ((result.m_type = Scalar::PromoteToMaxType(lhs, rhs)) != Scalar::e_void) {
    if (!rhs.IsZero() && result.m_type == Scalar::e_int) {
      result.m_integer = lhs.m_integer % rhs.m_integer;
      return result;
    }
  }
  result.m_type = Scalar::e_void;
  return result;
}

const Scalar lldb_private::operator^(Scalar lhs, Scalar rhs) {
  Scalar result;
  if ((result.m_type = Scalar::PromoteToMaxType(lhs, rhs)) != Scalar::e_void) {
    if (result.m_type == Scalar::e_int)
      result.m_integer = lhs.m_integer ^ rhs.m_integer;
    else
      result.m_type = Scalar::e_void;
  }
  return result;
}

const Scalar lldb_private::operator<<(const Scalar &lhs, const Scalar &rhs) {
  Scalar result = lhs;
  result <<= rhs;
  return result;
}

const Scalar lldb_private::operator>>(const Scalar &lhs, const Scalar &rhs) {
  Scalar result = lhs;
  result >>= rhs;
  return result;
}

Status Scalar::SetValueFromCString(const char *value_str, Encoding encoding,
                                   size_t byte_size) {
  Status error;
  if (value_str == nullptr || value_str[0] == '\0') {
    return Status::FromErrorString("Invalid c-string value string.");
  }
  switch (encoding) {
  case eEncodingInvalid:
    return Status::FromErrorString("Invalid encoding.");
    break;

  case eEncodingSint:
  case eEncodingUint: {
    llvm::StringRef str = value_str;
    bool is_signed = encoding == eEncodingSint;
    bool is_negative = is_signed && str.consume_front("-");
    APInt integer;
    if (str.getAsInteger(0, integer)) {
      error = Status::FromErrorStringWithFormatv(
          "'{0}' is not a valid integer string value", value_str);
      break;
    }
    bool fits;
    if (is_signed) {
      integer = integer.zext(integer.getBitWidth() + 1);
      if (is_negative)
        integer.negate();
      fits = integer.isSignedIntN(byte_size * 8);
    } else
      fits = integer.isIntN(byte_size * 8);
    if (!fits) {
      error = Status::FromErrorStringWithFormatv(
          "value {0} is too large to fit in a {1} byte integer value",
          value_str, byte_size);
      break;
    }
    m_type = e_int;
    m_integer =
        APSInt(std::move(integer), !is_signed).extOrTrunc(8 * byte_size);
    break;
  }

  case eEncodingIEEE754: {
    // FIXME: It's not possible to unambiguously map a byte size to a floating
    // point type. This function should be refactored to take an explicit
    // semantics argument.
    const llvm::fltSemantics &sem =
        byte_size <= 4 ? APFloat::IEEEsingle()
                       : byte_size <= 8 ? APFloat::IEEEdouble()
                                        : APFloat::x87DoubleExtended();
    APFloat f(sem);
    if (llvm::Expected<APFloat::opStatus> op =
            f.convertFromString(value_str, APFloat::rmNearestTiesToEven)) {
      m_type = e_float;
      m_float = std::move(f);
    } else
      error = Status::FromError(op.takeError());
    break;
  }

  case eEncodingVector:
    return Status::FromErrorString("vector encoding unsupported.");
    break;
  }
  if (error.Fail())
    m_type = e_void;

  return error;
}

Status Scalar::SetValueFromData(const DataExtractor &data,
                                lldb::Encoding encoding, size_t byte_size) {
  Status error;
  switch (encoding) {
  case lldb::eEncodingInvalid:
    return Status::FromErrorString("invalid encoding");
    break;
  case lldb::eEncodingVector:
    return Status::FromErrorString("vector encoding unsupported");
    break;
  case lldb::eEncodingUint:
  case lldb::eEncodingSint: {
    if (data.GetByteSize() < byte_size)
      return Status::FromErrorString("insufficient data");
    m_type = e_int;
    m_integer =
        APSInt(APInt::getZero(8 * byte_size), encoding == eEncodingUint);
    if (data.GetByteOrder() == endian::InlHostByteOrder()) {
      llvm::LoadIntFromMemory(m_integer, data.GetDataStart(), byte_size);
    } else {
      std::vector<uint8_t> buffer(byte_size);
      std::copy_n(data.GetDataStart(), byte_size, buffer.rbegin());
      llvm::LoadIntFromMemory(m_integer, buffer.data(), byte_size);
    }
    break;
  }
  case lldb::eEncodingIEEE754: {
    lldb::offset_t offset = 0;

    if (byte_size == sizeof(float))
      operator=(data.GetFloat(&offset));
    else if (byte_size == sizeof(double))
      operator=(data.GetDouble(&offset));
    else if (byte_size == sizeof(long double))
      operator=(data.GetLongDouble(&offset));
    else
      return Status::FromErrorStringWithFormatv(
          "unsupported float byte size: {0}", static_cast<uint64_t>(byte_size));
  } break;
  }

  return error;
}

bool Scalar::SignExtend(uint32_t sign_bit_pos) {
  const uint32_t max_bit_pos = GetByteSize() * 8;

  if (sign_bit_pos < max_bit_pos) {
    switch (m_type) {
    case Scalar::e_void:
    case Scalar::e_float:
      return false;

    case Scalar::e_int:
      if (sign_bit_pos < (max_bit_pos - 1)) {
        llvm::APInt sign_bit = llvm::APInt::getSignMask(sign_bit_pos + 1);
        llvm::APInt bitwize_and = m_integer & sign_bit;
        if (bitwize_and.getBoolValue()) {
          llvm::APInt mask =
              ~(sign_bit) + llvm::APInt(m_integer.getBitWidth(), 1);
          m_integer |= APSInt(std::move(mask), m_integer.isUnsigned());
        }
        return true;
      }
      break;
    }
  }
  return false;
}

size_t Scalar::GetAsMemoryData(void *dst, size_t dst_len,
                               lldb::ByteOrder dst_byte_order,
                               Status &error) const {
  // Get a data extractor that points to the native scalar data
  DataExtractor data;
  if (!GetData(data)) {
    error = Status::FromErrorString("invalid scalar value");
    return 0;
  }

  const size_t src_len = data.GetByteSize();

  // Prepare a memory buffer that contains some or all of the register value
  const size_t bytes_copied =
      data.CopyByteOrderedData(0,               // src offset
                               src_len,         // src length
                               dst,             // dst buffer
                               dst_len,         // dst length
                               dst_byte_order); // dst byte order
  if (bytes_copied == 0)
    error = Status::FromErrorString("failed to copy data");

  return bytes_copied;
}

bool Scalar::ExtractBitfield(uint32_t bit_size, uint32_t bit_offset) {
  if (bit_size == 0)
    return true;

  switch (m_type) {
  case Scalar::e_void:
  case Scalar::e_float:
    break;

  case Scalar::e_int:
    m_integer >>= bit_offset;
    m_integer = m_integer.extOrTrunc(bit_size).extOrTrunc(8 * GetByteSize());
    return true;
  }
  return false;
}

llvm::APFloat Scalar::CreateAPFloatFromAPSInt(lldb::BasicType basic_type) {
  switch (basic_type) {
  case lldb::eBasicTypeFloat:
    return llvm::APFloat(
        m_integer.isSigned()
            ? llvm::APIntOps::RoundSignedAPIntToFloat(m_integer)
            : llvm::APIntOps::RoundAPIntToFloat(m_integer));
  case lldb::eBasicTypeDouble:
    // No way to get more precision at the moment.
  case lldb::eBasicTypeLongDouble:
    return llvm::APFloat(
        m_integer.isSigned()
            ? llvm::APIntOps::RoundSignedAPIntToDouble(m_integer)
            : llvm::APIntOps::RoundAPIntToDouble(m_integer));
  default:
    const llvm::fltSemantics &sem = APFloat::IEEEsingle();
    return llvm::APFloat::getNaN(sem);
  }
}

llvm::APFloat Scalar::CreateAPFloatFromAPFloat(lldb::BasicType basic_type) {
  switch (basic_type) {
  case lldb::eBasicTypeFloat: {
    bool loses_info;
    m_float.convert(llvm::APFloat::IEEEsingle(),
                    llvm::APFloat::rmNearestTiesToEven, &loses_info);
    return m_float;
  }
  case lldb::eBasicTypeDouble:
    // No way to get more precision at the moment.
  case lldb::eBasicTypeLongDouble: {
    bool loses_info;
    m_float.convert(llvm::APFloat::IEEEdouble(),
                    llvm::APFloat::rmNearestTiesToEven, &loses_info);
    return m_float;
  }
  default:
    const llvm::fltSemantics &sem = APFloat::IEEEsingle();
    return llvm::APFloat::getNaN(sem);
  }
}

APFloat::cmpResult lldb_private::compare(Scalar lhs, Scalar rhs) {
  // If either entry is void then we can just compare the types
  if (lhs.m_type == Scalar::e_void || rhs.m_type == Scalar::e_void)
    return lhs.m_type == rhs.m_type ? APFloat::cmpEqual : APFloat::cmpUnordered;

  switch (Scalar::PromoteToMaxType(lhs, rhs)) {
  case Scalar::e_void:
    break;
  case Scalar::e_int:
    if (lhs.m_integer < rhs.m_integer)
      return APFloat::cmpLessThan;
    if (lhs.m_integer > rhs.m_integer)
      return APFloat::cmpGreaterThan;
    return APFloat::cmpEqual;
  case Scalar::e_float:
    return lhs.m_float.compare(rhs.m_float);
  }
  return APFloat::cmpUnordered;
}

bool lldb_private::operator==(const Scalar &lhs, const Scalar &rhs) {
  return compare(lhs, rhs) == APFloat::cmpEqual;
}

bool lldb_private::operator!=(const Scalar &lhs, const Scalar &rhs) {
  return compare(lhs, rhs) != APFloat::cmpEqual;
}

bool lldb_private::operator<(const Scalar &lhs, const Scalar &rhs) {
  return compare(lhs, rhs) == APFloat::cmpLessThan;
}

bool lldb_private::operator<=(const Scalar &lhs, const Scalar &rhs) {
  APFloat::cmpResult Res = compare(lhs, rhs);
  return Res == APFloat::cmpLessThan || Res == APFloat::cmpEqual;
}

bool lldb_private::operator>(const Scalar &lhs, const Scalar &rhs) {
  return compare(lhs, rhs) == APFloat::cmpGreaterThan;
}

bool lldb_private::operator>=(const Scalar &lhs, const Scalar &rhs) {
  APFloat::cmpResult Res = compare(lhs, rhs);
  return Res == APFloat::cmpGreaterThan || Res == APFloat::cmpEqual;
}

bool Scalar::ClearBit(uint32_t bit) {
  switch (m_type) {
  case e_void:
    break;
  case e_int:
    m_integer.clearBit(bit);
    return true;
  case e_float:
    break;
  }
  return false;
}

bool Scalar::SetBit(uint32_t bit) {
  switch (m_type) {
  case e_void:
    break;
  case e_int:
    m_integer.setBit(bit);
    return true;
  case e_float:
    break;
  }
  return false;
}

llvm::raw_ostream &lldb_private::operator<<(llvm::raw_ostream &os, const Scalar &scalar) {
  StreamString s;
  scalar.GetValue(s, /*show_type*/ true);
  return os << s.GetString();
}
