Files
swift-mirror/lib/Demangling/RemanglerBase.h
Alastair Houghton 5ece45a61f [Remangler] Improve performance by caching hashes.
The deepHash() function gets called repeatedly as we descend the
node tree, which results in O(n^2) behaviour because we're traversing
entire node subtree from each node we try substitution in, in order
to calculate the hash.

Fix by adding a hash table for hashes, so that we can look up hashes
we've already computed.

This appears to yield a 26.8% saving in local tests.

rdar://125739630
2024-04-04 16:09:12 +01:00

198 lines
5.9 KiB
C++

//===--- Demangler.h - String to Node-Tree Demangling -----------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file contains shared code between the old and new remanglers.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_DEMANGLING_BASEREMANGLER_H
#define SWIFT_DEMANGLING_BASEREMANGLER_H
#include "swift/Demangling/Demangler.h"
#include "swift/Demangling/NamespaceMacros.h"
#include <unordered_map>
using namespace swift::Demangle;
using llvm::StringRef;
namespace swift {
namespace Demangle {
SWIFT_BEGIN_INLINE_NAMESPACE
#define RETURN_IF_ERROR(x) \
do { \
ManglingError err = (x); \
if (!err.isSuccess()) \
return err; \
} while (0)
// An entry in the remangler's substitution map.
class SubstitutionEntry {
Node *TheNode = nullptr;
size_t StoredHash = 0;
bool treatAsIdentifier = false;
public:
void setNode(Node *node, bool treatAsIdentifier, size_t hash) {
this->treatAsIdentifier = treatAsIdentifier;
TheNode = node;
StoredHash = hash;
}
struct Hasher {
size_t operator()(const SubstitutionEntry &entry) const {
return entry.StoredHash;
}
};
bool isEmpty() const { return !TheNode; }
bool matches(Node *node, bool treatAsIdentifier) const {
return node == TheNode && treatAsIdentifier == this->treatAsIdentifier;
}
size_t hash() const { return StoredHash; }
private:
friend bool operator==(const SubstitutionEntry &lhs,
const SubstitutionEntry &rhs) {
if (lhs.StoredHash != rhs.StoredHash)
return false;
if (lhs.treatAsIdentifier != rhs.treatAsIdentifier)
return false;
if (lhs.treatAsIdentifier) {
return identifierEquals(lhs.TheNode, rhs.TheNode);
}
return lhs.deepEquals(lhs.TheNode, rhs.TheNode);
}
static bool identifierEquals(Node *lhs, Node *rhs);
bool deepEquals(Node *lhs, Node *rhs) const;
};
/// The output string for the Remangler.
///
/// It's allocating the string with the provided Factory.
class RemanglerBuffer {
CharVector Stream;
NodeFactory &Factory;
public:
RemanglerBuffer(NodeFactory &Factory) : Factory(Factory) {
Stream.init(Factory, 32);
}
void reset(size_t toPos) { Stream.resetSize(toPos); }
StringRef strRef() const { return Stream.str(); }
RemanglerBuffer &operator<<(char c) & {
Stream.push_back(c, Factory);
return *this;
}
RemanglerBuffer &operator<<(llvm::StringRef Value) & {
Stream.append(Value, Factory);
return *this;
}
RemanglerBuffer &operator<<(int n) & {
Stream.append(n, Factory);
return *this;
}
RemanglerBuffer &operator<<(unsigned n) & {
Stream.append((unsigned long long)n, Factory);
return *this;
}
RemanglerBuffer &operator<<(unsigned long n) & {
Stream.append((unsigned long long)n, Factory);
return *this;
}
RemanglerBuffer &operator<<(unsigned long long n) & {
Stream.append(n, Factory);
return *this;
}
};
/// The base class for the old and new remangler.
class RemanglerBase {
protected:
// Used to allocate temporary nodes and the output string (in Buffer).
NodeFactory &Factory;
// Recursively calculating the node hashes can be expensive if the node tree
// is deep, so we keep a hash table mapping (Node *, treatAsIdentifier) pairs
// to hashes.
static const size_t HashHashCapacity = 512; // Must be a power of 2
static const size_t HashHashMaxProbes = 8;
SubstitutionEntry HashHash[HashHashCapacity] = {};
// An efficient hash-map implementation in the spirit of llvm's SmallPtrSet:
// The first 16 substitutions are stored in an inline-allocated array to avoid
// malloc calls in the common case.
// Lookup is still reasonable fast because there are max 16 elements in the
// array.
static const size_t InlineSubstCapacity = 16;
SubstitutionEntry InlineSubstitutions[InlineSubstCapacity];
size_t NumInlineSubsts = 0;
// The "overflow" for InlineSubstitutions. Only if InlineSubstitutions is
// full, new substitutions are stored in OverflowSubstitutions.
std::unordered_map<SubstitutionEntry, unsigned, SubstitutionEntry::Hasher>
OverflowSubstitutions;
RemanglerBuffer Buffer;
protected:
RemanglerBase(NodeFactory &Factory)
: Factory(Factory), Buffer(Factory) { }
/// Compute the hash for a node.
size_t hashForNode(Node *node, bool treatAsIdentifier = false);
/// Construct a SubstitutionEntry for a given node.
/// This will look in the HashHash to see if we already know the hash,
/// to avoid having to walk the entire subtree.
SubstitutionEntry entryForNode(Node *node, bool treatAsIdentifier = false);
/// Find a substitution and return its index.
/// Returns -1 if no substitution is found.
int findSubstitution(const SubstitutionEntry &entry);
/// Adds a substitution.
void addSubstitution(const SubstitutionEntry &entry);
/// Resets the output string buffer to \p toPos.
void resetBuffer(size_t toPos) { Buffer.reset(toPos); }
public:
/// Append a custom string to the output buffer.
void append(StringRef str) { Buffer << str; }
StringRef getBufferStr() const { return Buffer.strRef(); }
std::string str() {
return getBufferStr().str();
}
};
SWIFT_END_INLINE_NAMESPACE
} // end namespace Demangle
} // end namespace swift
#endif // SWIFT_DEMANGLING_BASEREMANGLER_H