blob: 0747073a7b37e2a5fd7cacd4dd84d539b78785da [file] [log] [blame]
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- mode: C++ -*-
//
// Copyright 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: Giuliano Procida
#ifndef STG_EQUALITY_H_
#define STG_EQUALITY_H_
#include <cstddef>
#include <map>
#include <vector>
#include "graph.h"
#include "scc.h"
namespace stg {
// Node equality algorithm. This only cares about node and edge attributes and
// is blind to node identity. It is generic over the equality cache which is fed
// information about equality results and queried for the same. Different
// implementations are possible depending on the needs of the caller and the
// guaranteed invariants.
template <typename EqualityCache>
struct Equals {
Equals(const Graph& graph, EqualityCache& equality_cache)
: graph(graph), equality_cache(equality_cache) {}
bool operator()(Id id1, Id id2) {
const Pair comparison = {id1, id2};
// Check if the comparison has an already known result.
const auto check = equality_cache.Query(comparison);
if (check.has_value()) {
return check.value();
}
// Record the comparison with Strongly-Connected Component finder.
auto handle = scc.Open(comparison);
if (!handle) {
// Already open.
//
// Return a dummy true outcome.
return true;
}
// Comparison opened, need to close it before returning.
bool result = graph.Apply2<bool>(*this, id1, id2);
// Check for a complete Strongly-Connected Component.
auto comparisons = scc.Close(*handle);
if (comparisons.empty()) {
// Note that result is tentative as the SCC is still open.
return result;
}
// Closed SCC.
//
// Note that result is the conjunction of every equality in the SCC via the
// DFS spanning tree.
if (result) {
equality_cache.AllSame(comparisons);
} else {
equality_cache.AllDifferent(comparisons);
}
return result;
}
bool operator()(const std::vector<Id>& ids1, const std::vector<Id>& ids2) {
bool result = ids1.size() == ids2.size();
for (size_t ix = 0; result && ix < ids1.size(); ++ix) {
result = (*this)(ids1[ix], ids2[ix]);
}
return result;
}
template <typename Key>
bool operator()(const std::map<Key, Id>& ids1,
const std::map<Key, Id>& ids2) {
bool result = ids1.size() == ids2.size();
auto it1 = ids1.begin();
auto it2 = ids2.begin();
const auto end1 = ids1.end();
const auto end2 = ids2.end();
while (result && it1 != end1 && it2 != end2) {
result = it1->first == it2->first
&& (*this)(it1->second, it2->second);
++it1;
++it2;
}
return result && it1 == end1 && it2 == end2;
}
bool operator()(const Special& x1, const Special& x2) {
return x1.kind == x2.kind;
}
bool operator()(const PointerReference& x1,
const PointerReference& x2) {
return x1.kind == x2.kind
&& (*this)(x1.pointee_type_id, x2.pointee_type_id);
}
bool operator()(const PointerToMember& x1, const PointerToMember& x2) {
return (*this)(x1.containing_type_id, x2.containing_type_id)
&& (*this)(x1.pointee_type_id, x2.pointee_type_id);
}
bool operator()(const Typedef& x1, const Typedef& x2) {
return x1.name == x2.name
&& (*this)(x1.referred_type_id, x2.referred_type_id);
}
bool operator()(const Qualified& x1, const Qualified& x2) {
return x1.qualifier == x2.qualifier
&& (*this)(x1.qualified_type_id, x2.qualified_type_id);
}
bool operator()(const Primitive& x1, const Primitive& x2) {
return x1.name == x2.name
&& x1.encoding == x2.encoding
&& x1.bytesize == x2.bytesize;
}
bool operator()(const Array& x1, const Array& x2) {
return x1.number_of_elements == x2.number_of_elements
&& (*this)(x1.element_type_id, x2.element_type_id);
}
bool operator()(const BaseClass& x1, const BaseClass& x2) {
return x1.offset == x2.offset
&& x1.inheritance == x2.inheritance
&& (*this)(x1.type_id, x2.type_id);
}
bool operator()(const Method& x1, const Method& x2) {
return x1.mangled_name == x2.mangled_name
&& x1.name == x2.name
&& x1.vtable_offset == x2.vtable_offset
&& (*this)(x1.type_id, x2.type_id);
}
bool operator()(const Member& x1, const Member& x2) {
return x1.name == x2.name
&& x1.offset == x2.offset
&& x1.bitsize == x2.bitsize
&& (*this)(x1.type_id, x2.type_id);
}
bool operator()(const StructUnion& x1, const StructUnion& x2) {
const auto& definition1 = x1.definition;
const auto& definition2 = x2.definition;
bool result = x1.kind == x2.kind
&& x1.name == x2.name
&& definition1.has_value() == definition2.has_value();
if (result && definition1.has_value()) {
result = definition1->bytesize == definition2->bytesize
&& (*this)(definition1->base_classes, definition2->base_classes)
&& (*this)(definition1->methods, definition2->methods)
&& (*this)(definition1->members, definition2->members);
}
return result;
}
bool operator()(const Enumeration& x1, const Enumeration& x2) {
const auto& definition1 = x1.definition;
const auto& definition2 = x2.definition;
bool result = x1.name == x2.name
&& definition1.has_value() == definition2.has_value();
if (result && definition1.has_value()) {
result = (*this)(definition1->underlying_type_id,
definition2->underlying_type_id)
&& definition1->enumerators == definition2->enumerators;
}
return result;
}
bool operator()(const Function& x1, const Function& x2) {
return (*this)(x1.parameters, x2.parameters)
&& (*this)(x1.return_type_id, x2.return_type_id);
}
bool operator()(const ElfSymbol& x1, const ElfSymbol& x2) {
bool result = x1.symbol_name == x2.symbol_name
&& x1.version_info == x2.version_info
&& x1.is_defined == x2.is_defined
&& x1.symbol_type == x2.symbol_type
&& x1.binding == x2.binding
&& x1.visibility == x2.visibility
&& x1.crc == x2.crc
&& x1.ns == x2.ns
&& x1.full_name == x2.full_name
&& x1.type_id.has_value() == x2.type_id.has_value();
if (result && x1.type_id.has_value()) {
result = (*this)(x1.type_id.value(), x2.type_id.value());
}
return result;
}
bool operator()(const Interface& x1, const Interface& x2) {
return (*this)(x1.symbols, x2.symbols)
&& (*this)(x1.types, x2.types);
}
bool Mismatch() {
return false;
}
const Graph& graph;
EqualityCache& equality_cache;
SCC<Pair> scc;
};
} // namespace stg
#endif // STG_EQUALITY_H_