| // Copyright 2018 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_EXPR_EXPR_VALUE_SOURCE_H_ |
| #define SRC_DEVELOPER_DEBUG_ZXDB_EXPR_EXPR_VALUE_SOURCE_H_ |
| |
| #include <stdint.h> |
| |
| #include "src/developer/debug/shared/register_id.h" |
| #include "src/developer/debug/zxdb/common/int128_t.h" |
| |
| namespace zxdb { |
| |
| // Holds the source of a value. This allows taking the address of an object stored in an ExprValue |
| // ("&foo"), and for updating the contents of variables (currently not supported yet). |
| class ExprValueSource { |
| public: |
| // Where this value came from. |
| enum class Type { |
| // No source, this is the result of some computation. |
| kTemporary, |
| |
| // The value lived in memory at the specified address. |
| kMemory, |
| |
| // The value came from the specified CPU register. |
| kRegister, |
| |
| // The value is known to be constant and can not be changed. The difference between this and |
| // "temporary" is really just messaging since neither can be modified. |
| kConstant, |
| |
| // This value came from more than one place. The optimizer can sometimes split things up, |
| // for example, a pair might be put into two CPU registers, one for each value. There can also |
| // be composite CPU/memory ones if something is in memory, but a modification to that is |
| // only stored in a register. |
| // |
| // We currently don't support this and this enum indicates that the value can't be modified. |
| // But we can message that it could be with additional feature work. |
| // |
| // TODO(bug 39630) the ExprValueSource should probably have a vector of sub-regions, each with |
| // their own ExprValueSource. When we extract structure members, also extract the correct |
| // sub-region(s). |
| kComposite, |
| }; |
| |
| // Returns a string corresponding to the given type, "register", "temporary", etc. |
| static const char* TypeToString(Type t); |
| |
| // Indicates an unknown, temporary (the output of "i + 4"), or constant source. |
| explicit ExprValueSource(Type type = Type::kTemporary) : type_(type) {} |
| |
| // Initializes indicating a memory address and optional bitfield information. |
| explicit ExprValueSource(uint64_t address, uint32_t bit_size = 0, uint32_t bit_shift = 0) |
| : type_(Type::kMemory), address_(address), bit_size_(bit_size), bit_shift_(bit_shift) {} |
| |
| // Initializes indicating a register and optional bitfield information. The register does not have |
| // to be a canonical register. |
| explicit ExprValueSource(debug::RegisterID id, uint32_t bit_size = 0, uint32_t bit_shift = 0) |
| : type_(Type::kRegister), register_id_(id), bit_size_(bit_size), bit_shift_(bit_shift) {} |
| |
| Type type() const { return type_; } |
| |
| bool is_bitfield() const { return bit_size_ != 0; } |
| |
| // Valid when type_ == kAddress. |
| uint64_t address() const { return address_; } |
| |
| // Valid when type_ == kRegister. |
| debug::RegisterID register_id() const { return register_id_; } |
| |
| // Number of bits used for bitfields. 0 means it is not a bitfield and all bits are used. |
| uint32_t bit_size() const { return bit_size_; } |
| |
| // Number of bits to shift to the left to get the storage location. This is the offset of the low |
| // bit. Note that this is different than the DWARF definition. |
| // |
| // If a bitfield occupies bits 3-6 (inclusive) of a 32-bit integer: |
| // |
| // high low |
| // 3 2 1 0 |
| // 10987654 32109876 54321098 76543210 |
| // [--] |
| // <-- bit_shift |
| // |
| // Then the bit_size() will be 4 and the bit_shift() will be 3. |
| // |
| // The memory layout will be the result of doing the shift and mask and memcpy-ing out which |
| // will reorder the bytes in little-endian. |
| uint32_t bit_shift() const { return bit_shift_; } |
| |
| // Returns a new ExprValueSource pointing to the given offset inside of this one. If this one is |
| // not in memory, the returned one will be the same. |
| // |
| // When computing offsets of bitfields, the shifts are just added to any existing one, but the bit |
| // size (if given) will overwrite any existing one. |
| ExprValueSource GetOffsetInto(uint32_t offset, uint32_t new_bit_size = 0, |
| uint32_t bit_shift = 0) const { |
| if (type_ == Type::kMemory) { |
| return ExprValueSource(address_ + offset, new_bit_size == 0 ? bit_size_ : new_bit_size, |
| bit_shift_ + bit_shift); |
| } |
| return ExprValueSource(); |
| } |
| |
| // Writes the |new_value| over some |existing| value, taking into account the bit size and |
| // shift information from this ExprValueSource. The returned value can be used to update the |
| // register or memory for a bitfield. |
| // |
| // This ExprValueSource must be a bitfield (is_bitfield() == true) for this to be called. |
| uint128_t SetBits(uint128_t existing, uint128_t new_value) const; |
| |
| bool operator==(const ExprValueSource& other) const { |
| return type_ == other.type_ && address_ == other.address_ && bit_size_ == other.bit_size_ && |
| bit_shift_ == other.bit_shift_; |
| } |
| bool operator!=(const ExprValueSource& other) const { return !operator==(other); } |
| |
| private: |
| Type type_ = Type::kTemporary; |
| uint64_t address_ = 0; |
| debug::RegisterID register_id_ = debug::RegisterID::kUnknown; |
| |
| uint32_t bit_size_ = 0; |
| uint32_t bit_shift_ = 0; |
| }; |
| |
| } // namespace zxdb |
| |
| #endif // SRC_DEVELOPER_DEBUG_ZXDB_EXPR_EXPR_VALUE_SOURCE_H_ |