This patch removes all of the Set.* methods from Status. This cleanup is part of a series of patches that make it harder use the anti-pattern of keeping a long-lives Status object around and updating it while dropping any errors it contains on the floor. This patch is largely NFC, the more interesting next steps this enables is to: 1. remove Status.Clear() 2. assert that Status::operator=() never overwrites an error 3. remove Status::operator=() Note that step (2) will bring 90% of the benefits for users, and step (3) will dramatically clean up the error handling code in various places. In the end my goal is to convert all APIs that are of the form ` ResultTy DoFoo(Status& error) ` to ` llvm::Expected<ResultTy> DoFoo() ` How to read this patch? The interesting changes are in Status.h and Status.cpp, all other changes are mostly ` perl -pi -e 's/\.SetErrorString/ = Status::FromErrorString/g' $(git grep -l SetErrorString lldb/source) ` plus the occasional manual cleanup.
939 lines
24 KiB
C++
939 lines
24 KiB
C++
//===-- 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 = 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);
|
|
}
|
|
}
|
|
|
|
bool lldb_private::operator==(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;
|
|
|
|
llvm::APFloat::cmpResult result;
|
|
switch (Scalar::PromoteToMaxType(lhs, rhs)) {
|
|
case Scalar::e_void:
|
|
break;
|
|
case Scalar::e_int:
|
|
return lhs.m_integer == rhs.m_integer;
|
|
case Scalar::e_float:
|
|
result = lhs.m_float.compare(rhs.m_float);
|
|
if (result == llvm::APFloat::cmpEqual)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool lldb_private::operator!=(const Scalar &lhs, const Scalar &rhs) {
|
|
return !(lhs == rhs);
|
|
}
|
|
|
|
bool lldb_private::operator<(Scalar lhs, Scalar rhs) {
|
|
if (lhs.m_type == Scalar::e_void || rhs.m_type == Scalar::e_void)
|
|
return false;
|
|
|
|
llvm::APFloat::cmpResult result;
|
|
switch (Scalar::PromoteToMaxType(lhs, rhs)) {
|
|
case Scalar::e_void:
|
|
break;
|
|
case Scalar::e_int:
|
|
return lhs.m_integer < rhs.m_integer;
|
|
case Scalar::e_float:
|
|
result = lhs.m_float.compare(rhs.m_float);
|
|
if (result == llvm::APFloat::cmpLessThan)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool lldb_private::operator<=(const Scalar &lhs, const Scalar &rhs) {
|
|
return !(rhs < lhs);
|
|
}
|
|
|
|
bool lldb_private::operator>(const Scalar &lhs, const Scalar &rhs) {
|
|
return rhs < lhs;
|
|
}
|
|
|
|
bool lldb_private::operator>=(const Scalar &lhs, const Scalar &rhs) {
|
|
return !(lhs < rhs);
|
|
}
|
|
|
|
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();
|
|
}
|