blob: c4a283b1deac2e4200fa4dd7727f513d3ff172eb [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_DEVELOPER_DEBUG_ZXDB_SYMBOLS_DWARF_STACK_ENTRY_H_
#define SRC_DEVELOPER_DEBUG_ZXDB_SYMBOLS_DWARF_STACK_ENTRY_H_
#include <iosfwd>
#include "src/developer/debug/zxdb/common/int128_t.h"
#include "src/developer/debug/zxdb/symbols/base_type.h"
namespace zxdb {
// Represents an entry in the stack for evaluating DWARF 5 expressions.
//
// DWARF 5 introduced "typed" stack entries. Previously, all values were of a generic type. This
// means that every entry has a value plus an optional type which is a reference to a "base" type
//
// "Each element of the stack has a type and a value, and can represent a value of any supported
// base type of the target machine. Instead of a base type, elements can have a generic type,
// which is an integral type that has the size of an address on the target machine and unspecified
// signedness."
//
// We treat these different values as either signed, unsigned, float, or double. The generic type
// and bools are stored as unsigned.
class DwarfStackEntry {
public:
// The DWARF spec says the stack entry "can represent a value of any supported base type of the
// target machine". We need to support x87 long doubles (80 bits) and XMM registers (128 bits).
// Generally the XMM registers used for floating point use only the low 64 bits and long doubles
// are very uncommon, but using 128 bits here covers the edge cases better. The ARM "v" registers
// (128 bits) are similar.
//
// The YMM (256 bit) and ZMM (512 bit) x64 reigisters aren't currently representable in DWARF
// expressions so larger numbers are unnecessary.
using SignedType = int128_t;
using UnsignedType = uint128_t;
explicit DwarfStackEntry(UnsignedType generic_value);
// The sign of the BaseType in the first argument must match the sign of the second argument.
DwarfStackEntry(fxl::RefPtr<BaseType> type, SignedType value);
DwarfStackEntry(fxl::RefPtr<BaseType> type, UnsignedType value); // type can be null for generic.
DwarfStackEntry(fxl::RefPtr<BaseType> type, float value);
DwarfStackEntry(fxl::RefPtr<BaseType> type, double value);
// Comparison for unit testing. If types are present, the base type enum and byte size are
// compared, but not the name nor the identity of the type record.
bool operator==(const DwarfStackEntry& other) const;
bool operator!=(const DwarfStackEntry& other) const { return !operator==(other); }
bool is_generic() const { return !type_; }
const BaseType* type() const { return type_.get(); } // Possibly null.
fxl::RefPtr<BaseType> type_ref() const { return type_; } // Possibly null.
// Returns the size in bytes of this value.
size_t GetByteSize() const;
UnsignedType unsigned_value() const { return unsigned_value_; }
SignedType signed_value() const { return signed_value_; }
float float_value() const { return float_value_; }
double double_value() const { return double_value_; }
// These functions also accept null BaseType pointers which are counted as generic.
static bool TreatAsSigned(const BaseType* type);
static bool TreatAsUnsigned(const BaseType* type);
static bool TreatAsFloat(const BaseType* type);
static bool TreatAsDouble(const BaseType* type);
bool TreatAsSigned() const { return TreatAsSigned(type_.get()); }
bool TreatAsUnsigned() const { return TreatAsUnsigned(type_.get()); }
bool TreatAsFloat() const { return TreatAsFloat(type_.get()); }
bool TreatAsDouble() const { return TreatAsDouble(type_.get()); }
bool is_integral() const { return TreatAsSigned() || TreatAsUnsigned(); }
// Returns true if the value is within the machine epsilon of 0 for the current type.
bool IsZero() const;
// Returns true if the two stack entries have the same type, either the same base type, or they
// are both generic. Most arithmetic operations require them to be the same.
bool SameTypeAs(const DwarfStackEntry& other) const;
std::string GetTypeDescription() const;
private:
// Null indicates "generic".
fxl::RefPtr<BaseType> type_;
// When a type is given, only the low X bytes are relevant (where X is the byte size of the given
// type). However, the value should be a valid integer (the unused bits will be 0 in the unsigned
// case, and sign-extended in the signed case).
//
// Generic values are treated as unsigned.
//
// We do not currently support non-integral stack entries. These are not currently generated by
// the compiler.
//
// NOTE: Some users expect this to be a union! If you know the byte size of the result, some
// users extract the output as an unsigned and memcpy it to the result to avoid type-checking.
union {
UnsignedType unsigned_value_; // Address, boolean, unsigned, unsigned char, UTF.
SignedType signed_value_; // Signed, signed char.
float float_value_; // Float, 32-bit.
double double_value_; // Float, 64-bit.
};
};
// For test output.
std::ostream& operator<<(std::ostream& out, const DwarfStackEntry& entry);
} // namespace zxdb
#endif // SRC_DEVELOPER_DEBUG_ZXDB_SYMBOLS_DWARF_STACK_ENTRY_H_