blob: 80dccc78c94083bd399ec4c8b9dfd3cc1252b68a [file] [log] [blame]
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- mode: C++ -*-
//
// Copyright 2020-2022 Google LLC
//
// Licensed under the Apache License v2.0 with LLVM Exceptions (the
// "License"); you may not use this file except in compliance with the
// License. You may obtain a copy of the License at
//
// https://llvm.org/LICENSE.txt
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: Maria Teguiani
// Author: Giuliano Procida
// Author: Ignes Simeonova
#ifndef STG_STG_H_
#define STG_STG_H_
#include <cstddef>
#include <cstdint>
#include <functional>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <sstream>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>
#include "crc.h"
#include "id.h"
#include "scc.h"
namespace stg {
struct Node;
template <typename Kind, typename... Args>
std::unique_ptr<Node> Make(Args&&... args) {
return std::make_unique<Kind>(std::forward<Args>(args)...);
}
// Concrete graph type.
class Graph {
public:
const Node& Get(Id id) const;
bool Is(Id) const;
Id Allocate();
void Set(Id id, std::unique_ptr<Node> node);
Id Add(std::unique_ptr<Node> node);
private:
std::vector<std::unique_ptr<Node>> nodes_;
};
// A Parameter refers to a variable declared in the function declaration, used
// in the context of Function.
struct Parameter {
const std::string name;
const Id type_id;
};
enum class Qualifier { CONST, VOLATILE, RESTRICT };
using Qualifiers = std::set<Qualifier>;
std::ostream& operator<<(std::ostream& os, Qualifier qualifier);
using Comparison = std::pair<std::optional<Id>, std::optional<Id>>;
enum class Precedence { NIL, POINTER, ARRAY_FUNCTION, ATOMIC };
enum class Side { LEFT, RIGHT };
class Name {
public:
explicit Name(const std::string& name)
: left_(name), precedence_(Precedence::NIL), right_() {}
Name(const std::string& left, Precedence precedence, const std::string& right)
: left_(left), precedence_(precedence), right_(right) {}
Name Add(Side side, Precedence precedence, const std::string& text) const;
Name Qualify(const Qualifiers& qualifiers) const;
std::ostream& Print(std::ostream& os) const;
private:
std::string left_;
Precedence precedence_;
std::string right_;
};
std::ostream& operator<<(std::ostream& os, const Name& name);
using NameCache = std::unordered_map<Id, Name>;
Id ResolveQualifiers(const Graph& graph, Id id, Qualifiers& qualifiers);
Id ResolveTypedefs(
const Graph& graph, Id id, std::vector<std::string>& typedefs);
const Name& GetDescription(const Graph& graph, NameCache& names, Id node);
std::string GetResolvedDescription(const Graph& graph, NameCache& names, Id id);
struct DiffDetail {
DiffDetail(const std::string& text, const std::optional<Comparison>& edge)
: text_(text), edge_(edge) {}
std::string text_;
std::optional<Comparison> edge_;
};
struct Diff {
// This diff node corresponds to an entity that is reportable, if it or any of
// its children (excluding reportable ones) has changed.
bool holds_changes = false;
// This diff node contains a local (non-recursive) change.
bool has_changes = false;
std::vector<DiffDetail> details;
void Add(const std::string& text,
const std::optional<Comparison>& comparison) {
details.emplace_back(text, comparison);
}
};
struct Result {
// Used when two nodes cannot be meaningfully compared.
Result& MarkIncomparable() {
equals_ = false;
diff_.has_changes = true;
return *this;
}
// Used when a node attribute has changed.
void AddNodeDiff(const std::string& text) {
equals_ = false;
diff_.has_changes = true;
diff_.Add(text, {});
}
// Used when a node attribute may have changed.
template <typename T>
void MaybeAddNodeDiff(
const std::string& text, const T& before, const T& after) {
if (before != after) {
std::ostringstream os;
os << text << " changed from " << before << " to " << after;
AddNodeDiff(os.str());
}
}
// Used when a node attribute may have changed, lazy version.
template <typename T>
void MaybeAddNodeDiff(std::function<void(std::ostream&)> text,
const T& before, const T& after) {
if (before != after) {
std::ostringstream os;
text(os);
os << " changed from " << before << " to " << after;
AddNodeDiff(os.str());
}
}
// Used when node attributes are optional values.
template <typename T>
void MaybeAddNodeDiff(const std::string& text, const std::optional<T>& before,
const std::optional<T>& after) {
if (before && after) {
MaybeAddNodeDiff(text, *before, *after);
} else if (before) {
std::ostringstream os;
os << text << *before << " was removed";
AddNodeDiff(os.str());
} else if (after) {
std::ostringstream os;
os << text << *after << " was added";
AddNodeDiff(os.str());
}
}
// Used when an edge has been removed or added.
void AddEdgeDiff(const std::string& text, const Comparison& comparison) {
equals_ = false;
diff_.Add(text, {comparison});
}
// Used when an edge to a possible comparison is present.
void MaybeAddEdgeDiff(const std::string& text,
const std::pair<bool, std::optional<Comparison>>& p) {
equals_ &= p.first;
const auto& comparison = p.second;
if (comparison)
diff_.Add(text, comparison);
}
// Used when an edge to a possible comparison is present, lazy version.
void MaybeAddEdgeDiff(std::function<void(std::ostream&)> text,
const std::pair<bool, std::optional<Comparison>>& p) {
equals_ &= p.first;
const auto& comparison = p.second;
if (comparison) {
std::ostringstream os;
text(os);
diff_.Add(os.str(), comparison);
}
}
bool equals_ = true;
Diff diff_;
};
struct HashComparison {
size_t operator()(const Comparison& comparison) const {
size_t seed = 0;
std::hash<std::optional<Id>> h;
combine_hash(seed, h(comparison.first));
combine_hash(seed, h(comparison.second));
return seed;
}
static void combine_hash(size_t& seed, size_t hash) {
seed ^= hash + 0x9e3779b97f4a7c15 + (seed << 12) + (seed >> 4);
}
};
using Outcomes = std::unordered_map<Comparison, Diff, HashComparison>;
struct State {
explicit State(const Graph& g) : graph(g) {}
const Graph& graph;
std::unordered_map<Comparison, bool, HashComparison> known;
Outcomes outcomes;
Outcomes provisional;
SCC<Comparison, HashComparison> scc;
};
struct Node {
virtual ~Node() = default;
// as<Target>() provides a method to delegate downcasting to the base class,
// instead of needing to use dynamic_cast in a local context. If the type is
// not correct, an exception will be thrown.
template <typename Target>
const Target& as() const {
static_assert(std::is_convertible<Target*, Node*>::value,
"Target must publically inherit Node");
return dynamic_cast<const Target&>(*this);
}
// Separate qualifiers from underlying type.
//
// The caller must always be prepared to receive a different type as
// qualifiers are sometimes discarded.
virtual std::optional<Id> ResolveQualifier(Qualifiers& qualifiers) const;
virtual std::optional<Id> ResolveTypedef(
std::vector<std::string>& typedefs) const;
virtual std::string MatchingKey(const Graph& graph) const;
virtual Name MakeDescription(const Graph& graph, NameCache& names) const = 0;
virtual std::string GetKindDescription() const;
virtual Result Equals(State& state, const Node& other) const = 0;
};
Comparison Removed(State& state, Id node);
Comparison Added(State& state, Id node);
std::pair<bool, std::optional<Comparison>> Compare(
State& state, Id node1, const Id node2);
struct Void : Node {
Name MakeDescription(const Graph& graph, NameCache& names) const final;
Result Equals(State& state, const Node& other) const final;
};
struct Variadic : Node {
Name MakeDescription(const Graph& graph, NameCache& names) const final;
Result Equals(State& state, const Node& other) const final;
};
struct PointerReference : Node {
enum class Kind {
POINTER,
LVALUE_REFERENCE,
RVALUE_REFERENCE,
};
PointerReference(Kind kind, Id pointee_type_id)
: kind(kind), pointee_type_id(pointee_type_id) {}
Name MakeDescription(const Graph& graph, NameCache& names) const final;
Result Equals(State& state, const Node& other) const final;
const Kind kind;
const Id pointee_type_id;
};
std::ostream& operator<<(std::ostream& os, PointerReference::Kind kind);
struct Typedef : Node {
Typedef(const std::string& name, Id referred_type_id)
: name(name), referred_type_id(referred_type_id) {}
Name MakeDescription(const Graph& graph, NameCache& names) const final;
Result Equals(State& state, const Node& other) const final;
std::optional<Id> ResolveTypedef(
std::vector<std::string>& typedefs) const final;
const std::string name;
const Id referred_type_id;
};
struct Qualified : Node {
Qualified(Qualifier qualifier, Id qualified_type_id)
: qualifier(qualifier), qualified_type_id(qualified_type_id) {}
Name MakeDescription(const Graph& graph, NameCache& names) const final;
Result Equals(State& state, const Node& other) const final;
std::optional<Id> ResolveQualifier(Qualifiers& qualifiers) const final;
const Qualifier qualifier;
const Id qualified_type_id;
};
struct Integer : Node {
enum class Encoding {
BOOLEAN,
SIGNED_INTEGER,
UNSIGNED_INTEGER,
SIGNED_CHARACTER,
UNSIGNED_CHARACTER,
UTF,
};
Integer(const std::string& name, Encoding encoding, uint32_t bitsize,
uint32_t bytesize)
: name(name), encoding(encoding), bitsize(bitsize), bytesize(bytesize) {}
Name MakeDescription(const Graph& graph, NameCache& names) const final;
Result Equals(State& state, const Node& other) const final;
const std::string name;
const Encoding encoding;
// bitsize gives the semantics of the field. bytesize gives the
// storage size, and is equal to bitsize / 8 rounded up.
const uint32_t bitsize;
const uint32_t bytesize;
};
struct Array : Node {
Array(uint64_t number_of_elements, Id element_type_id)
: number_of_elements(number_of_elements),
element_type_id(element_type_id) {}
Name MakeDescription(const Graph& graph, NameCache& names) const final;
Result Equals(State& state, const Node& other) const final;
std::optional<Id> ResolveQualifier(Qualifiers& qualifiers) const final;
const uint64_t number_of_elements;
const Id element_type_id;
};
struct BaseClass : Node {
enum class Inheritance { NON_VIRTUAL, VIRTUAL };
BaseClass(Id type_id, uint64_t offset, Inheritance inheritance)
: type_id(type_id), offset(offset), inheritance(inheritance) {}
std::string GetKindDescription() const final;
Name MakeDescription(const Graph& graph, NameCache& names) const final;
Result Equals(State& state, const Node& other) const final;
std::string MatchingKey(const Graph& graph) const final;
const Id type_id;
const uint64_t offset;
const Inheritance inheritance;
};
std::ostream& operator<<(std::ostream& os, BaseClass::Inheritance inheritance);
struct Member : Node {
Member(const std::string& name, Id type_id, uint64_t offset, uint64_t bitsize)
: name(name), type_id(type_id), offset(offset), bitsize(bitsize) {}
std::string GetKindDescription() const final;
Name MakeDescription(const Graph& graph, NameCache& names) const final;
Result Equals(State& state, const Node& other) const final;
std::string MatchingKey(const Graph& graph) const final;
const std::string name;
const Id type_id;
const uint64_t offset;
const uint64_t bitsize;
};
struct Method : Node {
enum class Kind { NON_VIRTUAL, STATIC, VIRTUAL };
Method(const std::string& mangled_name, const std::string& name, Kind kind,
const std::optional<uint64_t> vtable_offset, Id type_id)
: mangled_name(mangled_name), name(name), kind(kind),
vtable_offset(vtable_offset), type_id(type_id) {}
std::string GetKindDescription() const final;
Name MakeDescription(const Graph& graph, NameCache& names) const final;
Result Equals(State& state, const Node& other) const final;
std::string MatchingKey(const Graph& graph) const final;
const std::string mangled_name;
const std::string name;
const Kind kind;
const std::optional<uint64_t> vtable_offset;
const Id type_id;
};
std::ostream& operator<<(std::ostream& os, Method::Kind kind);
struct StructUnion : Node {
enum class Kind { CLASS, STRUCT, UNION };
struct Definition {
const uint64_t bytesize;
const std::vector<Id> base_classes;
const std::vector<Id> methods;
const std::vector<Id> members;
};
StructUnion(Kind kind, const std::string& name) : kind(kind), name(name) {}
StructUnion(Kind kind, const std::string& name, uint64_t bytesize,
const std::vector<Id>& base_classes,
const std::vector<Id>& methods, const std::vector<Id>& members)
: kind(kind), name(name),
definition({bytesize, base_classes, methods, members}) {}
Name MakeDescription(const Graph& graph, NameCache& names) const final;
Result Equals(State& state, const Node& other) const final;
std::string MatchingKey(const Graph& graph) const final;
const Kind kind;
const std::string name;
const std::optional<Definition> definition;
};
std::ostream& operator<<(std::ostream& os, StructUnion::Kind kind);
struct Enumeration : Node {
using Enumerators = std::vector<std::pair<std::string, int64_t>>;
struct Definition {
const uint32_t bytesize;
const Enumerators enumerators;
};
Enumeration(const std::string& name) : name(name) {}
Enumeration(const std::string& name, uint32_t bytesize,
const Enumerators& enumerators)
: name(name), definition({bytesize, enumerators}) {}
Name MakeDescription(const Graph& graph, NameCache& names) const final;
Result Equals(State& state, const Node& other) const final;
std::vector<std::pair<std::string, size_t>> GetEnumNames() const;
const std::string name;
const std::optional<Definition> definition;
};
struct Function : Node {
Function(Id return_type_id, const std::vector<Parameter>& parameters)
: return_type_id(return_type_id), parameters(parameters) {}
Name MakeDescription(const Graph& graph, NameCache& names) const final;
Result Equals(State& state, const Node& other) const final;
std::optional<Id> ResolveQualifier(Qualifiers& qualifiers) const final;
const Id return_type_id;
const std::vector<Parameter> parameters;
};
struct ElfSymbol : Node {
enum class SymbolType { OBJECT, FUNCTION, COMMON, TLS };
enum class Binding { GLOBAL, LOCAL, WEAK, GNU_UNIQUE };
enum class Visibility { DEFAULT, PROTECTED, HIDDEN, INTERNAL };
ElfSymbol(const std::string& symbol_name,
const std::string& version,
bool is_default_version,
bool is_defined,
SymbolType symbol_type,
Binding binding,
Visibility visibility,
std::optional<CRC> crc,
std::optional<Id> type_id,
const std::optional<std::string>& full_name)
: symbol_name(symbol_name),
version(version),
is_default_version(is_default_version),
is_defined(is_defined),
symbol_type(symbol_type),
binding(binding),
visibility(visibility),
crc(crc),
type_id(type_id),
full_name(full_name) {}
std::string GetKindDescription() const final;
Name MakeDescription(const Graph& graph, NameCache& names) const final;
Result Equals(State& state, const Node& other) const final;
const std::string symbol_name;
const std::string version;
const bool is_default_version;
const bool is_defined;
const SymbolType symbol_type;
const Binding binding;
const Visibility visibility;
const std::optional<CRC> crc;
const std::optional<Id> type_id;
const std::optional<std::string> full_name;
};
std::ostream& operator<<(std::ostream& os, ElfSymbol::SymbolType);
std::ostream& operator<<(std::ostream& os, ElfSymbol::Binding);
std::ostream& operator<<(std::ostream& os, ElfSymbol::Visibility);
struct SymbolKey {
const std::string path;
const std::string name;
// auto operator<=>(const SymbolKey&) const = default
int compare(const SymbolKey& other) const {
int result = path.compare(other.path);
if (result)
return result;
return name.compare(other.name);
}
bool operator==(const SymbolKey& other) const {
return compare(other) == 0;
}
bool operator!=(const SymbolKey& other) const {
return compare(other) != 0;
}
bool operator<(const SymbolKey& other) const {
return compare(other) < 0;
}
bool operator>(const SymbolKey& other) const {
return compare(other) > 0;
}
bool operator<=(const SymbolKey& other) const {
return compare(other) <= 0;
}
bool operator>=(const SymbolKey& other) const {
return compare(other) >= 0;
}
};
std::ostream& operator<<(std::ostream& os, const SymbolKey& key);
struct Symbols : Node {
Symbols(const std::map<SymbolKey, Id>& symbols) : symbols(symbols) {}
std::string GetKindDescription() const final;
Name MakeDescription(const Graph& graph, NameCache& names) const final;
Result Equals(State& state, const Node& other) const final;
const std::map<SymbolKey, Id> symbols;
};
std::ostream& operator<<(std::ostream& os, Integer::Encoding encoding);
} // namespace stg
#endif // STG_STG_H_