This renames the primary methods for creating a zero value to `getZero` instead of `getNullValue` and renames predicates like `isAllOnesValue` to simply `isAllOnes`. This achieves two things: 1) This starts standardizing predicates across the LLVM codebase, following (in this case) ConstantInt. The word "Value" doesn't convey anything of merit, and is missing in some of the other things. 2) Calling an integer "null" doesn't make any sense. The original sin here is mine and I've regretted it for years. This moves us to calling it "zero" instead, which is correct! APInt is widely used and I don't think anyone is keen to take massive source breakage on anything so core, at least not all in one go. As such, this doesn't actually delete any entrypoints, it "soft deprecates" them with a comment. Included in this patch are changes to a bunch of the codebase, but there are more. We should normalize SelectionDAG and other APIs as well, which would make the API change more mechanical. Differential Revision: https://reviews.llvm.org/D109483
899 lines
22 KiB
C++
899 lines
22 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 <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() / 8);
|
|
case e_float:
|
|
return m_float.bitcastToAPInt().getBitWidth() / 8;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool Scalar::IsZero() const {
|
|
switch (m_type) {
|
|
case e_void:
|
|
break;
|
|
case e_int:
|
|
return m_integer.isNullValue();
|
|
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') {
|
|
error.SetErrorString("Invalid c-string value string.");
|
|
return error;
|
|
}
|
|
switch (encoding) {
|
|
case eEncodingInvalid:
|
|
error.SetErrorString("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.SetErrorStringWithFormatv(
|
|
"'{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.SetErrorStringWithFormatv(
|
|
"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:
|
|
error.SetErrorString("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:
|
|
error.SetErrorString("invalid encoding");
|
|
break;
|
|
case lldb::eEncodingVector:
|
|
error.SetErrorString("vector encoding unsupported");
|
|
break;
|
|
case lldb::eEncodingUint:
|
|
case lldb::eEncodingSint: {
|
|
if (data.GetByteSize() < byte_size)
|
|
return Status("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
|
|
error.SetErrorStringWithFormat("unsupported float byte size: %" PRIu64 "",
|
|
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 (max_bit_pos == sign_bit_pos)
|
|
return true;
|
|
else 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.SetErrorString("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.SetErrorString("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;
|
|
}
|
|
|
|
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();
|
|
}
|