mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
202 lines
5.5 KiB
C++
202 lines
5.5 KiB
C++
#pragma once
|
|
|
|
//
|
|
// This file is distributed under the MIT License. See LICENSE.md for details.
|
|
//
|
|
|
|
#include <set>
|
|
|
|
#include "revng/Support/Assert.h"
|
|
|
|
template<bool StaticallyEnabled>
|
|
class Logger;
|
|
|
|
/// Different types of accesses to the CPU State Variables (CSVs), with a set of
|
|
/// possible offsets.
|
|
class CSVOffsets {
|
|
|
|
private:
|
|
using OffsetSet = std::set<int64_t>;
|
|
|
|
public:
|
|
using iterator = OffsetSet::iterator;
|
|
using const_iterator = OffsetSet::const_iterator;
|
|
using size_type = OffsetSet::size_type;
|
|
|
|
public:
|
|
enum Kind {
|
|
Unknown,
|
|
Numeric,
|
|
KnownInPtr,
|
|
OutAndKnownInPtr,
|
|
UnknownInPtr,
|
|
OutAndUnknownInPtr,
|
|
};
|
|
|
|
static const char *toString(const Kind K) {
|
|
switch (K) {
|
|
case Unknown:
|
|
return "Unknown";
|
|
case Numeric:
|
|
return "Numeric";
|
|
case KnownInPtr:
|
|
return "KnownInPtr";
|
|
case OutAndKnownInPtr:
|
|
return "OutAndKnownInPtr";
|
|
case UnknownInPtr:
|
|
return "UnknownInPtr";
|
|
case OutAndUnknownInPtr:
|
|
return "OutAndUnknownInPtr";
|
|
default:
|
|
revng_abort();
|
|
}
|
|
return "";
|
|
}
|
|
|
|
private:
|
|
Kind OffsetKind;
|
|
OffsetSet Offsets;
|
|
|
|
public:
|
|
CSVOffsets() : OffsetKind(Kind::Numeric), Offsets() {}
|
|
CSVOffsets(Kind K) : OffsetKind(K), Offsets() {
|
|
// Useful for debug revng_assert(isUnknown(K) or isUnknownInPtr(K));
|
|
}
|
|
CSVOffsets(Kind K, int64_t O) : OffsetKind(K), Offsets({ O }) {
|
|
// Useful for debug revng_assert(not isUnknown(K) and not
|
|
// isUnknownInPtr(K));
|
|
}
|
|
CSVOffsets(Kind K, std::set<int64_t> O) : OffsetKind(K), Offsets(O) {
|
|
// Useful for debug revng_assert(not isUnknown(K) and not
|
|
// isUnknownInPtr(K));
|
|
}
|
|
|
|
public:
|
|
friend void writeToLog(Logger<true> &L, const CSVOffsets &O, int /*Ignore*/);
|
|
|
|
private:
|
|
explicit operator Kind() const { return OffsetKind; }
|
|
|
|
public:
|
|
iterator begin() { return Offsets.begin(); }
|
|
iterator end() { return Offsets.end(); }
|
|
|
|
const_iterator begin() const { return Offsets.cbegin(); }
|
|
const_iterator end() const { return Offsets.cend(); }
|
|
|
|
size_type size() const { return Offsets.size(); }
|
|
size_type empty() const { return Offsets.empty(); }
|
|
|
|
static bool isUnknown(const Kind K) { return K == Kind::Unknown; }
|
|
bool isUnknown() const { return isUnknown(OffsetKind); }
|
|
|
|
static bool isNumeric(const Kind K) { return K == Kind::Numeric; }
|
|
bool isNumeric() const { return isNumeric(OffsetKind); }
|
|
|
|
static bool isPtr(const Kind K) {
|
|
return not isNumeric(K) and not isUnknown(K);
|
|
}
|
|
bool isPtr() const { return isPtr(OffsetKind); }
|
|
|
|
static bool isOnlyInPtr(const Kind K) {
|
|
return K == Kind::UnknownInPtr or K == Kind::KnownInPtr;
|
|
}
|
|
bool isOnlyInPtr() const { return isOnlyInPtr(OffsetKind); }
|
|
|
|
static bool isInOutPtr(const Kind K) {
|
|
return isPtr(K) and not isOnlyInPtr(K);
|
|
}
|
|
bool isInOutPtr() const { return isInOutPtr(OffsetKind); }
|
|
|
|
static bool isUnknownInPtr(const Kind K) {
|
|
return K == Kind::UnknownInPtr or K == Kind::OutAndUnknownInPtr;
|
|
}
|
|
bool isUnknownInPtr() const { return isUnknownInPtr(OffsetKind); }
|
|
|
|
static bool isKnownInPtr(const Kind K) {
|
|
return isPtr(K) and not isUnknownInPtr(K);
|
|
}
|
|
bool isKnownInPtr() const { return isKnownInPtr(OffsetKind); }
|
|
|
|
static bool hasOffsetSet(const Kind K) {
|
|
return K == Kind::KnownInPtr or K == Kind::OutAndKnownInPtr
|
|
or K == Kind::Numeric;
|
|
}
|
|
bool hasOffsetSet() const { return hasOffsetSet(OffsetKind); }
|
|
|
|
enum Kind getKind() const { return OffsetKind; }
|
|
static enum Kind makeUnknown(const Kind K) {
|
|
switch (K) {
|
|
case Kind::Numeric:
|
|
return Kind::Unknown;
|
|
case Kind::KnownInPtr:
|
|
return Kind::UnknownInPtr;
|
|
case Kind::OutAndKnownInPtr:
|
|
return Kind::OutAndUnknownInPtr;
|
|
default:
|
|
revng_abort();
|
|
}
|
|
return K;
|
|
}
|
|
|
|
void insert(int64_t O) { Offsets.insert(O); }
|
|
void combine(const CSVOffsets &Other) {
|
|
Kind K0 = OffsetKind;
|
|
Kind K1 = Other.OffsetKind;
|
|
// For equal kinds just merge the offsets
|
|
if (K0 == K1) {
|
|
Offsets.insert(Other.Offsets.begin(), Other.Offsets.end());
|
|
return;
|
|
}
|
|
|
|
// If one is OutAndUnknownInPtr always return OutAndUnknownInPtr
|
|
if (K0 == Kind::OutAndUnknownInPtr or K1 == Kind::OutAndUnknownInPtr) {
|
|
OffsetKind = Kind::OutAndUnknownInPtr;
|
|
Offsets = {};
|
|
return;
|
|
}
|
|
|
|
if (K0 == Kind::UnknownInPtr or K1 == Kind::UnknownInPtr) {
|
|
// If K0 and K1 are equal, or one of them is a OnlyInPtr stay in
|
|
// UnknownInPtr, in all the other cases also access out, hence returning
|
|
// OutAndUnknownInPtr
|
|
if (isOnlyInPtr(K0) or isOnlyInPtr(K1) or K0 == K1)
|
|
OffsetKind = Kind::UnknownInPtr;
|
|
else
|
|
OffsetKind = Kind::OutAndUnknownInPtr;
|
|
|
|
Offsets = {};
|
|
return;
|
|
}
|
|
|
|
if (K0 == Kind::OutAndKnownInPtr or K1 == Kind::OutAndKnownInPtr) {
|
|
// If one is Unknown it wipes away the knowledge, otherwise we merge the
|
|
// Offsets
|
|
if (isUnknown(K0) or isUnknown(K1)) {
|
|
OffsetKind = Kind::OutAndUnknownInPtr;
|
|
Offsets = {};
|
|
} else {
|
|
OffsetKind = Kind::OutAndKnownInPtr;
|
|
Offsets.insert(Other.Offsets.begin(), Other.Offsets.end());
|
|
}
|
|
return;
|
|
}
|
|
|
|
{
|
|
bool KIP0 = K0 == Kind::KnownInPtr;
|
|
if (KIP0 or K1 == Kind::KnownInPtr) {
|
|
revng_assert(isNumeric(K0) or isNumeric(K1) or isUnknown(K0)
|
|
or isUnknown(K1));
|
|
OffsetKind = Kind::OutAndKnownInPtr;
|
|
if (not KIP0)
|
|
Offsets = Other.Offsets;
|
|
return;
|
|
}
|
|
}
|
|
revng_assert((isNumeric(K0) and isUnknown(K1))
|
|
or (isNumeric(K1) and isUnknown(K0)));
|
|
OffsetKind = Kind::Unknown;
|
|
Offsets = {};
|
|
}
|
|
};
|