Files
revng-revng/lib/Lift/CSVOffsets.h
2023-03-15 10:19:01 +01:00

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 = {};
}
};