Skip to content

Commit

Permalink
Common infrastructure for reading a profile remapping file and building
Browse files Browse the repository at this point in the history
a mangling remapper from it.

Differential Revision: https://reviews.llvm.org/D51246

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@342161 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
zygoloid committed Sep 13, 2018
1 parent 64a5134 commit 9d93ec6
Show file tree
Hide file tree
Showing 5 changed files with 312 additions and 0 deletions.
133 changes: 133 additions & 0 deletions include/llvm/Support/SymbolRemappingReader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
//===- SymbolRemappingReader.h - Read symbol remapping file -----*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains definitions needed for reading and applying symbol
// remapping files.
//
// Support is provided only for the Itanium C++ name mangling scheme for now.
//
// NOTE: If you are making changes to this file format, please remember
// to document them in the Clang documentation at
// tools/clang/docs/UsersManual.rst.
//
// File format
// -----------
//
// The symbol remappings are written as an ASCII text file. Blank lines and
// lines starting with a # are ignored. All other lines specify a kind of
// mangled name fragment, along with two fragments of that kind that should
// be treated as equivalent, separated by spaces.
//
// See http://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling for a
// description of the Itanium name mangling scheme.
//
// The accepted fragment kinds are:
//
// * name A <name>, such as 6foobar or St3__1
// * type A <type>, such as Ss or N4llvm9StringRefE
// * encoding An <encoding> (a complete mangling without the leading _Z)
//
// For example:
//
// # Ignore int / long differences to treat symbols from 32-bit and 64-bit
// # builds with differing size_t / ptrdiff_t / intptr_t as equivalent.
// type i l
// type j m
//
// # Ignore differences between libc++ and libstdc++, and between libstdc++'s
// # C++98 and C++11 ABIs.
// name 3std St3__1
// name 3std St7__cxx11
//
// # Remap a function overload to a specialization of a template (including
// # any local symbols declared within it).
// encoding N2NS1fEi N2NS1fIiEEvT_
//
// # Substitutions must be remapped separately from namespace 'std' for now.
// name Sa NSt3__19allocatorE
// name Sb NSt3__112basic_stringE
// type Ss NSt3__112basic_stringIcSt11char_traitsIcESaE
// # ...
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_SUPPORT_SYMBOLREMAPPINGREADER_H
#define LLVM_SUPPORT_SYMBOLREMAPPINGREADER_H

#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ItaniumManglingCanonicalizer.h"
#include "llvm/Support/MemoryBuffer.h"

namespace llvm {

class SymbolRemappingParseError : public ErrorInfo<SymbolRemappingParseError> {
public:
SymbolRemappingParseError(StringRef File, int64_t Line, Twine Message)
: File(File), Line(Line), Message(Message.str()) {}

void log(llvm::raw_ostream &OS) const override {
OS << File << ':' << Line << ": " << Message;
}
std::error_code convertToErrorCode() const override {
return llvm::inconvertibleErrorCode();
}

StringRef getFileName() const { return File; }
int64_t getLineNum() const { return Line; }
StringRef getMessage() const { return Message; }

static char ID;

private:
std::string File;
int64_t Line;
std::string Message;
};

/// Reader for symbol remapping files.
///
/// Remaps the symbol names in profile data to match those in the program
/// according to a set of rules specified in a given file.
class SymbolRemappingReader {
public:
/// Read remappings from the given buffer, which must live as long as
/// the remapper.
Error read(MemoryBuffer &B);

/// A Key represents an equivalence class of symbol names.
using Key = uintptr_t;

/// Construct a key for the given symbol, or return an existing one if an
/// equivalent name has already been inserted. The symbol name must live
/// as long as the remapper.
///
/// The result will be Key() if the name cannot be remapped (typically
/// because it is not a valid mangled name).
Key insert(StringRef FunctionName) {
return Canonicalizer.canonicalize(FunctionName);
}

/// Map the given symbol name into the key for the corresponding equivalence
/// class.
///
/// The result will typically be Key() if no equivalent symbol has been
/// inserted, but this is not guaranteed: a Key different from all keys ever
/// returned by \c insert may be returned instead.
Key lookup(StringRef FunctionName) {
return Canonicalizer.lookup(FunctionName);
}

private:
ItaniumManglingCanonicalizer Canonicalizer;
};

} // end namespace llvm

#endif // LLVM_SUPPORT_SYMBOLREMAPPINGREADER_H
1 change: 1 addition & 0 deletions lib/Support/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ add_llvm_library(LLVMSupport
StringPool.cpp
StringSaver.cpp
StringRef.cpp
SymbolRemappingReader.cpp
SystemUtils.cpp
TarWriter.cpp
TargetParser.cpp
Expand Down
81 changes: 81 additions & 0 deletions lib/Support/SymbolRemappingReader.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//===- SymbolRemappingReader.cpp - Read symbol remapping file -------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains definitions needed for reading and applying symbol
// remapping files.
//
//===----------------------------------------------------------------------===//

#include "llvm/Support/SymbolRemappingReader.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/LineIterator.h"

using namespace llvm;

char SymbolRemappingParseError::ID;

/// Load a set of name remappings from a text file.
///
/// See the documentation at the top of the file for an explanation of
/// the expected format.
Error SymbolRemappingReader::read(MemoryBuffer &B) {
line_iterator LineIt(B, /*SkipBlanks=*/true, '#');

auto ReportError = [&](Twine Msg) {
return llvm::make_error<SymbolRemappingParseError>(
B.getBufferIdentifier(), LineIt.line_number(), Msg);
};

for (; !LineIt.is_at_eof(); ++LineIt) {
StringRef Line = *LineIt;
Line = Line.ltrim(' ');
// line_iterator only detects comments starting in column 1.
if (Line.startswith("#") || Line.empty())
continue;

SmallVector<StringRef, 4> Parts;
Line.split(Parts, ' ', /*MaxSplits*/-1, /*KeepEmpty*/false);

if (Parts.size() != 3)
return ReportError("Expected 'kind mangled_name mangled_name', "
"found '" + Line + "'");

using FK = ItaniumManglingCanonicalizer::FragmentKind;
Optional<FK> FragmentKind = StringSwitch<Optional<FK>>(Parts[0])
.Case("name", FK::Name)
.Case("type", FK::Type)
.Case("encoding", FK::Encoding)
.Default(None);
if (!FragmentKind)
return ReportError("Invalid kind, expected 'name', 'type', or 'encoding',"
" found '" + Parts[0] + "'");

using EE = ItaniumManglingCanonicalizer::EquivalenceError;
switch (Canonicalizer.addEquivalence(*FragmentKind, Parts[1], Parts[2])) {
case EE::Success:
break;

case EE::ManglingAlreadyUsed:
return ReportError("Manglings '" + Parts[1] + "' and '" + Parts[2] + "' "
"have both been used in prior remappings. Move this "
"remapping earlier in the file.");

case EE::InvalidFirstMangling:
return ReportError("Could not demangle '" + Parts[1] + "' "
"as a <" + Parts[0] + ">; invalid mangling?");

case EE::InvalidSecondMangling:
return ReportError("Could not demangle '" + Parts[2] + "' "
"as a <" + Parts[0] + ">; invalid mangling?");
}
}

return Error::success();
}
1 change: 1 addition & 0 deletions unittests/Support/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ add_llvm_unittest(SupportTests
SpecialCaseListTest.cpp
StringPool.cpp
SwapByteOrderTest.cpp
SymbolRemappingReaderTest.cpp
TarWriterTest.cpp
TargetParserTest.cpp
TaskQueueTest.cpp
Expand Down
96 changes: 96 additions & 0 deletions unittests/Support/SymbolRemappingReaderTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
//===- unittests/Support/SymbolRemappingReaderTest.cpp --------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "llvm/Support/SymbolRemappingReader.h"
#include "llvm/Support/MemoryBuffer.h"
#include "gtest/gtest.h"

using namespace llvm;

namespace {
class SymbolRemappingReaderTest : public testing::Test {
public:
std::unique_ptr<MemoryBuffer> Buffer;
SymbolRemappingReader Reader;

std::string readWithErrors(StringRef Text, StringRef BufferName) {
Buffer = MemoryBuffer::getMemBuffer(Text, BufferName);
Error E = Reader.read(*Buffer);
EXPECT_TRUE((bool)E);
return toString(std::move(E));
}

void read(StringRef Text, StringRef BufferName) {
Buffer = MemoryBuffer::getMemBuffer(Text, BufferName);
Error E = Reader.read(*Buffer);
EXPECT_FALSE((bool)E);
}
};
} // unnamed namespace

TEST_F(SymbolRemappingReaderTest, ParseErrors) {
EXPECT_EQ(readWithErrors("error", "foo.map"),
"foo.map:1: Expected 'kind mangled_name mangled_name', "
"found 'error'");

EXPECT_EQ(readWithErrors("error m1 m2", "foo.map"),
"foo.map:1: Invalid kind, expected 'name', 'type', or 'encoding', "
"found 'error'");
}

TEST_F(SymbolRemappingReaderTest, DemanglingErrors) {
EXPECT_EQ(readWithErrors("type i banana", "foo.map"),
"foo.map:1: Could not demangle 'banana' as a <type>; "
"invalid mangling?");
EXPECT_EQ(readWithErrors("name i 1X", "foo.map"),
"foo.map:1: Could not demangle 'i' as a <name>; "
"invalid mangling?");
EXPECT_EQ(readWithErrors("name 1X 1fv", "foo.map"),
"foo.map:1: Could not demangle '1fv' as a <name>; "
"invalid mangling?");
EXPECT_EQ(readWithErrors("encoding 1fv 1f1gE", "foo.map"),
"foo.map:1: Could not demangle '1f1gE' as a <encoding>; "
"invalid mangling?");
}

TEST_F(SymbolRemappingReaderTest, BadMappingOrder) {
StringRef Map = R"(
# N::foo == M::bar
name N1N3fooE N1M3barE
# N:: == M::
name 1N 1M
)";
EXPECT_EQ(readWithErrors(Map, "foo.map"),
"foo.map:6: Manglings '1N' and '1M' have both been used in prior "
"remappings. Move this remapping earlier in the file.");
}

TEST_F(SymbolRemappingReaderTest, RemappingsAdded) {
StringRef Map = R"(
# A::foo == B::bar
name N1A3fooE N1B3barE
# int == long
type i l
# void f<int>() = void g<int>()
encoding 1fIiEvv 1gIiEvv
)";

read(Map, "foo.map");
auto Key = Reader.insert("_ZN1B3bar3bazIiEEvv");
EXPECT_NE(Key, SymbolRemappingReader::Key());
EXPECT_EQ(Key, Reader.lookup("_ZN1A3foo3bazIlEEvv"));
EXPECT_NE(Key, Reader.lookup("_ZN1C3foo3bazIlEEvv"));

Key = Reader.insert("_Z1fIiEvv");
EXPECT_NE(Key, SymbolRemappingReader::Key());
EXPECT_EQ(Key, Reader.lookup("_Z1gIlEvv"));
}

0 comments on commit 9d93ec6

Please sign in to comment.