From 3e51f754ad08cc453c5e7476bcc6e92743b68830 Mon Sep 17 00:00:00 2001 From: Alexey Samsonov Date: Wed, 9 Jul 2014 19:40:08 +0000 Subject: [PATCH] Decouple llvm::SpecialCaseList text representation and its LLVM IR semantics. Turn llvm::SpecialCaseList into a simple class that parses text files in a specified format and knows nothing about LLVM IR. Move this class into LLVMSupport library. Implement two users of this class: * DFSanABIList in DFSan instrumentation pass. * SanitizerBlacklist in Clang CodeGen library. The latter will be modified to use actual source-level information from frontend (source file names) instead of unstable LLVM IR things (LLVM Module identifier). Remove dependency edge from ClangCodeGen/ClangDriver to LLVMTransformUtils. No functionality change. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@212643 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/Support/SpecialCaseList.h | 94 +++++++ .../llvm/Transforms/Utils/SpecialCaseList.h | 114 --------- lib/Support/CMakeLists.txt | 1 + .../Utils => Support}/SpecialCaseList.cpp | 50 +--- .../Instrumentation/DataFlowSanitizer.cpp | 61 ++++- lib/Transforms/Utils/CMakeLists.txt | 1 - unittests/Support/CMakeLists.txt | 1 + unittests/Support/SpecialCaseListTest.cpp | 126 ++++++++++ unittests/Transforms/Utils/CMakeLists.txt | 1 - .../Transforms/Utils/SpecialCaseList.cpp | 232 ------------------ 10 files changed, 278 insertions(+), 403 deletions(-) create mode 100644 include/llvm/Support/SpecialCaseList.h delete mode 100644 include/llvm/Transforms/Utils/SpecialCaseList.h rename lib/{Transforms/Utils => Support}/SpecialCaseList.cpp (76%) create mode 100644 unittests/Support/SpecialCaseListTest.cpp delete mode 100644 unittests/Transforms/Utils/SpecialCaseList.cpp diff --git a/include/llvm/Support/SpecialCaseList.h b/include/llvm/Support/SpecialCaseList.h new file mode 100644 index 00000000000..192e15ab221 --- /dev/null +++ b/include/llvm/Support/SpecialCaseList.h @@ -0,0 +1,94 @@ +//===-- SpecialCaseList.h - special case list for sanitizers ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +//===----------------------------------------------------------------------===// +// +// This is a utility class used to parse user-provided text files with +// "special case lists" for code sanitizers. Such files are used to +// define "ABI list" for DataFlowSanitizer and blacklists for another sanitizers +// like AddressSanitizer or UndefinedBehaviorSanitizer. +// +// Empty lines and lines starting with "#" are ignored. All the rest lines +// should have the form: +// section:wildcard_expression[=category] +// If category is not specified, it is assumed to be empty string. +// Definitions of "section" and "category" are sanitizer-specific. For example, +// sanitizer blacklists support sections "src", "fun" and "global". +// Wildcard expressions define, respectively, source files, functions or +// globals which shouldn't be instrumented. +// Examples of categories: +// "functional": used in DFSan to list functions with pure functional +// semantics. +// "init": used in ASan blacklist to disable initialization-order bugs +// detection for certain globals or source files. +// Full special case list file example: +// --- +// # Blacklisted items: +// fun:*_ZN4base6subtle* +// global:*global_with_bad_access_or_initialization* +// global:*global_with_initialization_issues*=init +// type:*Namespace::ClassName*=init +// src:file_with_tricky_code.cc +// src:ignore-global-initializers-issues.cc=init +// +// # Functions with pure functional semantics: +// fun:cos=functional +// fun:sin=functional +// --- +// Note that the wild card is in fact an llvm::Regex, but * is automatically +// replaced with .* +// This is similar to the "ignore" feature of ThreadSanitizer. +// http://code.google.com/p/data-race-test/wiki/ThreadSanitizerIgnores +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_SPECIALCASELIST_H +#define LLVM_SUPPORT_SPECIALCASELIST_H + +#include "llvm/ADT/StringMap.h" + +namespace llvm { +class MemoryBuffer; +class Regex; +class StringRef; + +class SpecialCaseList { + public: + /// Parses the special case list from a file. If Path is empty, returns + /// an empty special case list. On failure, returns 0 and writes an error + /// message to string. + static SpecialCaseList *create(const StringRef Path, std::string &Error); + /// Parses the special case list from a memory buffer. On failure, returns + /// 0 and writes an error message to string. + static SpecialCaseList *create(const MemoryBuffer *MB, std::string &Error); + /// Parses the special case list from a file. On failure, reports a fatal + /// error. + static SpecialCaseList *createOrDie(const StringRef Path); + + ~SpecialCaseList(); + + /// Returns true, if special case list contains a line + /// @Section:=@Category + /// and @Query satisfies a wildcard expression . + bool inSection(const StringRef Section, const StringRef Query, + const StringRef Category = StringRef()) const; + + private: + SpecialCaseList(SpecialCaseList const &) LLVM_DELETED_FUNCTION; + SpecialCaseList &operator=(SpecialCaseList const &) LLVM_DELETED_FUNCTION; + + struct Entry; + StringMap > Entries; + + SpecialCaseList(); + /// Parses just-constructed SpecialCaseList entries from a memory buffer. + bool parse(const MemoryBuffer *MB, std::string &Error); +}; + +} // namespace llvm + +#endif // LLVM_SUPPORT_SPECIALCASELIST_H + diff --git a/include/llvm/Transforms/Utils/SpecialCaseList.h b/include/llvm/Transforms/Utils/SpecialCaseList.h deleted file mode 100644 index 508a6df5dce..00000000000 --- a/include/llvm/Transforms/Utils/SpecialCaseList.h +++ /dev/null @@ -1,114 +0,0 @@ -//===-- SpecialCaseList.h - special case list for sanitizers ----*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -//===----------------------------------------------------------------------===// -// -// This is a utility class for instrumentation passes (like AddressSanitizer -// or ThreadSanitizer) to avoid instrumenting some functions or global -// variables based on a user-supplied list. -// -// The list can also specify categories for specific globals, which can be used -// to instruct an instrumentation pass to treat certain functions or global -// variables in a specific way, such as by omitting certain aspects of -// instrumentation while keeping others, or informing the instrumentation pass -// that a specific uninstrumentable function has certain semantics, thus -// allowing the pass to instrument callers according to those semantics. -// -// For example, AddressSanitizer uses the "init" category for globals whose -// initializers should not be instrumented, but which in all other respects -// should be instrumented. -// -// Each line contains a prefix, followed by a colon and a wild card expression, -// followed optionally by an equals sign and an instrumentation-specific -// category. Empty lines and lines starting with "#" are ignored. -// --- -// # Blacklisted items: -// fun:*_ZN4base6subtle* -// global:*global_with_bad_access_or_initialization* -// global:*global_with_initialization_issues*=init -// type:*Namespace::ClassName*=init -// src:file_with_tricky_code.cc -// src:ignore-global-initializers-issues.cc=init -// -// # Functions with pure functional semantics: -// fun:cos=functional -// fun:sin=functional -// --- -// Note that the wild card is in fact an llvm::Regex, but * is automatically -// replaced with .* -// This is similar to the "ignore" feature of ThreadSanitizer. -// http://code.google.com/p/data-race-test/wiki/ThreadSanitizerIgnores -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TRANSFORMS_UTILS_SPECIALCASELIST_H -#define LLVM_TRANSFORMS_UTILS_SPECIALCASELIST_H - -#include "llvm/ADT/StringMap.h" - -namespace llvm { -class Function; -class GlobalAlias; -class GlobalVariable; -class MemoryBuffer; -class Module; -class Regex; -class StringRef; - -class SpecialCaseList { - public: - /// Parses the special case list from a file. If Path is empty, returns - /// an empty special case list. On failure, returns 0 and writes an error - /// message to string. - static SpecialCaseList *create(const StringRef Path, std::string &Error); - /// Parses the special case list from a memory buffer. On failure, returns - /// 0 and writes an error message to string. - static SpecialCaseList *create(const MemoryBuffer *MB, std::string &Error); - /// Parses the special case list from a file. On failure, reports a fatal - /// error. - static SpecialCaseList *createOrDie(const StringRef Path); - - ~SpecialCaseList(); - - /// Returns whether either this function or its source file are listed in the - /// given category, which may be omitted to search the empty category. - bool isIn(const Function &F, const StringRef Category = StringRef()) const; - - /// Returns whether this global, its type or its source file are listed in the - /// given category, which may be omitted to search the empty category. - bool isIn(const GlobalVariable &G, - const StringRef Category = StringRef()) const; - - /// Returns whether this global alias is listed in the given category, which - /// may be omitted to search the empty category. - /// - /// If GA aliases a function, the alias's name is matched as a function name - /// would be. Similarly, aliases of globals are matched like globals. - bool isIn(const GlobalAlias &GA, - const StringRef Category = StringRef()) const; - - /// Returns whether this module is listed in the given category, which may be - /// omitted to search the empty category. - bool isIn(const Module &M, const StringRef Category = StringRef()) const; - - private: - SpecialCaseList(SpecialCaseList const &) LLVM_DELETED_FUNCTION; - SpecialCaseList &operator=(SpecialCaseList const &) LLVM_DELETED_FUNCTION; - - struct Entry; - StringMap > Entries; - - SpecialCaseList(); - /// Parses just-constructed SpecialCaseList entries from a memory buffer. - bool parse(const MemoryBuffer *MB, std::string &Error); - - bool inSectionCategory(const StringRef Section, const StringRef Query, - const StringRef Category) const; -}; - -} // namespace llvm - -#endif // LLVM_TRANSFORMS_UTILS_SPECIALCASELIST_H diff --git a/lib/Support/CMakeLists.txt b/lib/Support/CMakeLists.txt index 033eae041c5..9ecd55935ea 100644 --- a/lib/Support/CMakeLists.txt +++ b/lib/Support/CMakeLists.txt @@ -47,6 +47,7 @@ add_llvm_library(LLVMSupport SmallPtrSet.cpp SmallVector.cpp SourceMgr.cpp + SpecialCaseList.cpp Statistic.cpp StreamableMemoryObject.cpp StringExtras.cpp diff --git a/lib/Transforms/Utils/SpecialCaseList.cpp b/lib/Support/SpecialCaseList.cpp similarity index 76% rename from lib/Transforms/Utils/SpecialCaseList.cpp rename to lib/Support/SpecialCaseList.cpp index 3d76a17abd9..d9921ac6920 100644 --- a/lib/Transforms/Utils/SpecialCaseList.cpp +++ b/lib/Support/SpecialCaseList.cpp @@ -14,15 +14,11 @@ // //===----------------------------------------------------------------------===// -#include "llvm/Transforms/Utils/SpecialCaseList.h" +#include "llvm/Support/SpecialCaseList.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSet.h" -#include "llvm/IR/DerivedTypes.h" -#include "llvm/IR/Function.h" -#include "llvm/IR/GlobalVariable.h" -#include "llvm/IR/Module.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Regex.h" #include "llvm/Support/raw_ostream.h" @@ -169,48 +165,8 @@ SpecialCaseList::~SpecialCaseList() { } } -bool SpecialCaseList::isIn(const Function& F, const StringRef Category) const { - return isIn(*F.getParent(), Category) || - inSectionCategory("fun", F.getName(), Category); -} - -static StringRef GetGlobalTypeString(const GlobalValue &G) { - // Types of GlobalVariables are always pointer types. - Type *GType = G.getType()->getElementType(); - // For now we support blacklisting struct types only. - if (StructType *SGType = dyn_cast(GType)) { - if (!SGType->isLiteral()) - return SGType->getName(); - } - return ""; -} - -bool SpecialCaseList::isIn(const GlobalVariable &G, - const StringRef Category) const { - return isIn(*G.getParent(), Category) || - inSectionCategory("global", G.getName(), Category) || - inSectionCategory("type", GetGlobalTypeString(G), Category); -} - -bool SpecialCaseList::isIn(const GlobalAlias &GA, - const StringRef Category) const { - if (isIn(*GA.getParent(), Category)) - return true; - - if (isa(GA.getType()->getElementType())) - return inSectionCategory("fun", GA.getName(), Category); - - return inSectionCategory("global", GA.getName(), Category) || - inSectionCategory("type", GetGlobalTypeString(GA), Category); -} - -bool SpecialCaseList::isIn(const Module &M, const StringRef Category) const { - return inSectionCategory("src", M.getModuleIdentifier(), Category); -} - -bool SpecialCaseList::inSectionCategory(const StringRef Section, - const StringRef Query, - const StringRef Category) const { +bool SpecialCaseList::inSection(const StringRef Section, const StringRef Query, + const StringRef Category) const { StringMap >::const_iterator I = Entries.find(Section); if (I == Entries.end()) return false; StringMap::const_iterator II = I->second.find(Category); diff --git a/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp b/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp index 7f468f79e22..3b9212a14e6 100644 --- a/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp +++ b/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp @@ -59,9 +59,9 @@ #include "llvm/IR/Value.h" #include "llvm/Pass.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/SpecialCaseList.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/Local.h" -#include "llvm/Transforms/Utils/SpecialCaseList.h" #include using namespace llvm; @@ -120,6 +120,51 @@ static cl::opt ClDebugNonzeroLabels( namespace { +StringRef GetGlobalTypeString(const GlobalValue &G) { + // Types of GlobalVariables are always pointer types. + Type *GType = G.getType()->getElementType(); + // For now we support blacklisting struct types only. + if (StructType *SGType = dyn_cast(GType)) { + if (!SGType->isLiteral()) + return SGType->getName(); + } + return ""; +} + +class DFSanABIList { + std::unique_ptr SCL; + + public: + DFSanABIList(SpecialCaseList *SCL) : SCL(SCL) {} + + /// Returns whether either this function or its source file are listed in the + /// given category. + bool isIn(const Function &F, const StringRef Category) const { + return isIn(*F.getParent(), Category) || + SCL->inSection("fun", F.getName(), Category); + } + + /// Returns whether this global alias is listed in the given category. + /// + /// If GA aliases a function, the alias's name is matched as a function name + /// would be. Similarly, aliases of globals are matched like globals. + bool isIn(const GlobalAlias &GA, const StringRef Category) const { + if (isIn(*GA.getParent(), Category)) + return true; + + if (isa(GA.getType()->getElementType())) + return SCL->inSection("fun", GA.getName(), Category); + + return SCL->inSection("global", GA.getName(), Category) || + SCL->inSection("type", GetGlobalTypeString(GA), Category); + } + + /// Returns whether this module is listed in the given category. + bool isIn(const Module &M, const StringRef Category) const { + return SCL->inSection("src", M.getModuleIdentifier(), Category); + } +}; + class DataFlowSanitizer : public ModulePass { friend struct DFSanFunction; friend class DFSanVisitor; @@ -190,7 +235,7 @@ class DataFlowSanitizer : public ModulePass { Constant *DFSanSetLabelFn; Constant *DFSanNonzeroLabelFn; MDNode *ColdCallWeights; - std::unique_ptr ABIList; + DFSanABIList ABIList; DenseMap UnwrappedFnMap; AttributeSet ReadOnlyNoneAttrs; @@ -395,11 +440,11 @@ bool DataFlowSanitizer::doInitialization(Module &M) { } bool DataFlowSanitizer::isInstrumented(const Function *F) { - return !ABIList->isIn(*F, "uninstrumented"); + return !ABIList.isIn(*F, "uninstrumented"); } bool DataFlowSanitizer::isInstrumented(const GlobalAlias *GA) { - return !ABIList->isIn(*GA, "uninstrumented"); + return !ABIList.isIn(*GA, "uninstrumented"); } DataFlowSanitizer::InstrumentedABI DataFlowSanitizer::getInstrumentedABI() { @@ -407,11 +452,11 @@ DataFlowSanitizer::InstrumentedABI DataFlowSanitizer::getInstrumentedABI() { } DataFlowSanitizer::WrapperKind DataFlowSanitizer::getWrapperKind(Function *F) { - if (ABIList->isIn(*F, "functional")) + if (ABIList.isIn(*F, "functional")) return WK_Functional; - if (ABIList->isIn(*F, "discard")) + if (ABIList.isIn(*F, "discard")) return WK_Discard; - if (ABIList->isIn(*F, "custom")) + if (ABIList.isIn(*F, "custom")) return WK_Custom; return WK_Warning; @@ -500,7 +545,7 @@ bool DataFlowSanitizer::runOnModule(Module &M) { if (!DL) return false; - if (ABIList->isIn(M, "skip")) + if (ABIList.isIn(M, "skip")) return false; if (!GetArgTLSPtr) { diff --git a/lib/Transforms/Utils/CMakeLists.txt b/lib/Transforms/Utils/CMakeLists.txt index e10ca90749c..fcf548f97c5 100644 --- a/lib/Transforms/Utils/CMakeLists.txt +++ b/lib/Transforms/Utils/CMakeLists.txt @@ -33,7 +33,6 @@ add_llvm_library(LLVMTransformUtils SimplifyIndVar.cpp SimplifyInstructions.cpp SimplifyLibCalls.cpp - SpecialCaseList.cpp UnifyFunctionExitNodes.cpp Utils.cpp ValueMapper.cpp diff --git a/unittests/Support/CMakeLists.txt b/unittests/Support/CMakeLists.txt index 08096a4a179..97c5c43aeb6 100644 --- a/unittests/Support/CMakeLists.txt +++ b/unittests/Support/CMakeLists.txt @@ -31,6 +31,7 @@ add_llvm_unittest(SupportTests RegexTest.cpp ScaledNumberTest.cpp SourceMgrTest.cpp + SpecialCaseListTest.cpp StringPool.cpp SwapByteOrderTest.cpp ThreadLocalTest.cpp diff --git a/unittests/Support/SpecialCaseListTest.cpp b/unittests/Support/SpecialCaseListTest.cpp new file mode 100644 index 00000000000..bb9c351415b --- /dev/null +++ b/unittests/Support/SpecialCaseListTest.cpp @@ -0,0 +1,126 @@ +//===- SpecialCaseListTest.cpp - Unit tests for SpecialCaseList -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SpecialCaseList.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +class SpecialCaseListTest : public ::testing::Test { +protected: + SpecialCaseList *makeSpecialCaseList(StringRef List, std::string &Error) { + std::unique_ptr MB(MemoryBuffer::getMemBuffer(List)); + return SpecialCaseList::create(MB.get(), Error); + } + + SpecialCaseList *makeSpecialCaseList(StringRef List) { + std::string Error; + SpecialCaseList *SCL = makeSpecialCaseList(List, Error); + assert(SCL); + assert(Error == ""); + return SCL; + } +}; + +TEST_F(SpecialCaseListTest, Basic) { + std::unique_ptr SCL( + makeSpecialCaseList("# This is a comment.\n" + "\n" + "src:hello\n" + "src:bye\n" + "src:hi=category\n" + "src:z*=category\n")); + EXPECT_TRUE(SCL->inSection("src", "hello")); + EXPECT_TRUE(SCL->inSection("src", "bye")); + EXPECT_TRUE(SCL->inSection("src", "hi", "category")); + EXPECT_TRUE(SCL->inSection("src", "zzzz", "category")); + EXPECT_FALSE(SCL->inSection("src", "hi")); + EXPECT_FALSE(SCL->inSection("fun", "hello")); + EXPECT_FALSE(SCL->inSection("src", "hello", "category")); +} + +TEST_F(SpecialCaseListTest, GlobalInitCompat) { + std::unique_ptr SCL( + makeSpecialCaseList("global:foo=init\n")); + EXPECT_FALSE(SCL->inSection("global", "foo")); + EXPECT_FALSE(SCL->inSection("global", "bar")); + EXPECT_TRUE(SCL->inSection("global", "foo", "init")); + EXPECT_FALSE(SCL->inSection("global", "bar", "init")); + + SCL.reset(makeSpecialCaseList("global-init:foo\n")); + EXPECT_FALSE(SCL->inSection("global", "foo")); + EXPECT_FALSE(SCL->inSection("global", "bar")); + EXPECT_TRUE(SCL->inSection("global", "foo", "init")); + EXPECT_FALSE(SCL->inSection("global", "bar", "init")); + + SCL.reset(makeSpecialCaseList("type:t2=init\n")); + EXPECT_FALSE(SCL->inSection("type", "t1")); + EXPECT_FALSE(SCL->inSection("type", "t2")); + EXPECT_FALSE(SCL->inSection("type", "t1", "init")); + EXPECT_TRUE(SCL->inSection("type", "t2", "init")); + + SCL.reset(makeSpecialCaseList("global-init-type:t2\n")); + EXPECT_FALSE(SCL->inSection("type", "t1")); + EXPECT_FALSE(SCL->inSection("type", "t2")); + EXPECT_FALSE(SCL->inSection("type", "t1", "init")); + EXPECT_TRUE(SCL->inSection("type", "t2", "init")); + + SCL.reset(makeSpecialCaseList("src:hello=init\n")); + EXPECT_FALSE(SCL->inSection("src", "hello")); + EXPECT_FALSE(SCL->inSection("src", "bye")); + EXPECT_TRUE(SCL->inSection("src", "hello", "init")); + EXPECT_FALSE(SCL->inSection("src", "bye", "init")); + + SCL.reset(makeSpecialCaseList("global-init-src:hello\n")); + EXPECT_FALSE(SCL->inSection("src", "hello")); + EXPECT_FALSE(SCL->inSection("src", "bye")); + EXPECT_TRUE(SCL->inSection("src", "hello", "init")); + EXPECT_FALSE(SCL->inSection("src", "bye", "init")); +} + +TEST_F(SpecialCaseListTest, Substring) { + std::unique_ptr SCL(makeSpecialCaseList("src:hello\n" + "fun:foo\n" + "global:bar\n")); + EXPECT_FALSE(SCL->inSection("src", "othello")); + EXPECT_FALSE(SCL->inSection("fun", "tomfoolery")); + EXPECT_FALSE(SCL->inSection("global", "bartender")); + + SCL.reset(makeSpecialCaseList("fun:*foo*\n")); + EXPECT_TRUE(SCL->inSection("fun", "tomfoolery")); + EXPECT_TRUE(SCL->inSection("fun", "foobar")); +} + +TEST_F(SpecialCaseListTest, InvalidSpecialCaseList) { + std::string Error; + EXPECT_EQ(nullptr, makeSpecialCaseList("badline", Error)); + EXPECT_EQ("Malformed line 1: 'badline'", Error); + EXPECT_EQ(nullptr, makeSpecialCaseList("src:bad[a-", Error)); + EXPECT_EQ("Malformed regex in line 1: 'bad[a-': invalid character range", + Error); + EXPECT_EQ(nullptr, makeSpecialCaseList("src:a.c\n" + "fun:fun(a\n", + Error)); + EXPECT_EQ("Malformed regex in line 2: 'fun(a': parentheses not balanced", + Error); + EXPECT_EQ(nullptr, SpecialCaseList::create("unexisting", Error)); + EXPECT_EQ(0U, Error.find("Can't open file 'unexisting':")); +} + +TEST_F(SpecialCaseListTest, EmptySpecialCaseList) { + std::unique_ptr SCL(makeSpecialCaseList("")); + EXPECT_FALSE(SCL->inSection("foo", "bar")); +} + +} + + diff --git a/unittests/Transforms/Utils/CMakeLists.txt b/unittests/Transforms/Utils/CMakeLists.txt index 60447bb5210..ffa1d49d380 100644 --- a/unittests/Transforms/Utils/CMakeLists.txt +++ b/unittests/Transforms/Utils/CMakeLists.txt @@ -9,5 +9,4 @@ add_llvm_unittest(UtilsTests Cloning.cpp IntegerDivision.cpp Local.cpp - SpecialCaseList.cpp ) diff --git a/unittests/Transforms/Utils/SpecialCaseList.cpp b/unittests/Transforms/Utils/SpecialCaseList.cpp deleted file mode 100644 index bcbca493af2..00000000000 --- a/unittests/Transforms/Utils/SpecialCaseList.cpp +++ /dev/null @@ -1,232 +0,0 @@ -//===- SpecialCaseList.cpp - Unit tests for SpecialCaseList ---------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "llvm/IR/Function.h" -#include "llvm/IR/LLVMContext.h" -#include "llvm/IR/Module.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Transforms/Utils/SpecialCaseList.h" -#include "gtest/gtest.h" - -using namespace llvm; - -namespace { - -class SpecialCaseListTest : public ::testing::Test { -protected: - Function *makeFunction(StringRef Name, Module &M) { - return Function::Create(FunctionType::get(Type::getVoidTy(Ctx), false), - GlobalValue::ExternalLinkage, - Name, - &M); - } - - GlobalVariable *makeGlobal(StringRef Name, StringRef StructName, Module &M) { - StructType *ST = - StructType::create(StructName, Type::getInt32Ty(Ctx), (Type*)nullptr); - return new GlobalVariable( - M, ST, false, GlobalValue::ExternalLinkage, nullptr, Name); - } - - GlobalAlias *makeAlias(StringRef Name, GlobalObject *Aliasee) { - return GlobalAlias::create(GlobalValue::ExternalLinkage, Name, Aliasee); - } - - SpecialCaseList *makeSpecialCaseList(StringRef List, std::string &Error) { - std::unique_ptr MB(MemoryBuffer::getMemBuffer(List)); - return SpecialCaseList::create(MB.get(), Error); - } - - SpecialCaseList *makeSpecialCaseList(StringRef List) { - std::string Error; - SpecialCaseList *SCL = makeSpecialCaseList(List, Error); - assert(SCL); - assert(Error == ""); - return SCL; - } - - LLVMContext Ctx; -}; - -TEST_F(SpecialCaseListTest, ModuleIsIn) { - Module M("hello", Ctx); - Function *F = makeFunction("foo", M); - GlobalVariable *GV = makeGlobal("bar", "t", M); - - std::unique_ptr SCL( - makeSpecialCaseList("# This is a comment.\n" - "\n" - "src:hello\n")); - EXPECT_TRUE(SCL->isIn(M)); - EXPECT_TRUE(SCL->isIn(*F)); - EXPECT_TRUE(SCL->isIn(*GV)); - - SCL.reset(makeSpecialCaseList("src:he*o\n")); - EXPECT_TRUE(SCL->isIn(M)); - EXPECT_TRUE(SCL->isIn(*F)); - EXPECT_TRUE(SCL->isIn(*GV)); - - SCL.reset(makeSpecialCaseList("src:hi\n")); - EXPECT_FALSE(SCL->isIn(M)); - EXPECT_FALSE(SCL->isIn(*F)); - EXPECT_FALSE(SCL->isIn(*GV)); -} - -TEST_F(SpecialCaseListTest, FunctionIsIn) { - Module M("hello", Ctx); - Function *Foo = makeFunction("foo", M); - Function *Bar = makeFunction("bar", M); - - std::unique_ptr SCL(makeSpecialCaseList("fun:foo\n")); - EXPECT_TRUE(SCL->isIn(*Foo)); - EXPECT_FALSE(SCL->isIn(*Bar)); - - SCL.reset(makeSpecialCaseList("fun:b*\n")); - EXPECT_FALSE(SCL->isIn(*Foo)); - EXPECT_TRUE(SCL->isIn(*Bar)); - - SCL.reset(makeSpecialCaseList("fun:f*\n" - "fun:bar\n")); - EXPECT_TRUE(SCL->isIn(*Foo)); - EXPECT_TRUE(SCL->isIn(*Bar)); - - SCL.reset(makeSpecialCaseList("fun:foo=functional\n")); - EXPECT_TRUE(SCL->isIn(*Foo, "functional")); - StringRef Category; - EXPECT_FALSE(SCL->isIn(*Bar, "functional")); -} - -TEST_F(SpecialCaseListTest, GlobalIsIn) { - Module M("hello", Ctx); - GlobalVariable *Foo = makeGlobal("foo", "t1", M); - GlobalVariable *Bar = makeGlobal("bar", "t2", M); - - std::unique_ptr SCL(makeSpecialCaseList("global:foo\n")); - EXPECT_TRUE(SCL->isIn(*Foo)); - EXPECT_FALSE(SCL->isIn(*Bar)); - EXPECT_FALSE(SCL->isIn(*Foo, "init")); - EXPECT_FALSE(SCL->isIn(*Bar, "init")); - - SCL.reset(makeSpecialCaseList("global:foo=init\n")); - EXPECT_FALSE(SCL->isIn(*Foo)); - EXPECT_FALSE(SCL->isIn(*Bar)); - EXPECT_TRUE(SCL->isIn(*Foo, "init")); - EXPECT_FALSE(SCL->isIn(*Bar, "init")); - - SCL.reset(makeSpecialCaseList("global-init:foo\n")); - EXPECT_FALSE(SCL->isIn(*Foo)); - EXPECT_FALSE(SCL->isIn(*Bar)); - EXPECT_TRUE(SCL->isIn(*Foo, "init")); - EXPECT_FALSE(SCL->isIn(*Bar, "init")); - - SCL.reset(makeSpecialCaseList("type:t2=init\n")); - EXPECT_FALSE(SCL->isIn(*Foo)); - EXPECT_FALSE(SCL->isIn(*Bar)); - EXPECT_FALSE(SCL->isIn(*Foo, "init")); - EXPECT_TRUE(SCL->isIn(*Bar, "init")); - - SCL.reset(makeSpecialCaseList("global-init-type:t2\n")); - EXPECT_FALSE(SCL->isIn(*Foo)); - EXPECT_FALSE(SCL->isIn(*Bar)); - EXPECT_FALSE(SCL->isIn(*Foo, "init")); - EXPECT_TRUE(SCL->isIn(*Bar, "init")); - - SCL.reset(makeSpecialCaseList("src:hello=init\n")); - EXPECT_FALSE(SCL->isIn(*Foo)); - EXPECT_FALSE(SCL->isIn(*Bar)); - EXPECT_TRUE(SCL->isIn(*Foo, "init")); - EXPECT_TRUE(SCL->isIn(*Bar, "init")); - - SCL.reset(makeSpecialCaseList("global-init-src:hello\n")); - EXPECT_FALSE(SCL->isIn(*Foo)); - EXPECT_FALSE(SCL->isIn(*Bar)); - EXPECT_TRUE(SCL->isIn(*Foo, "init")); - EXPECT_TRUE(SCL->isIn(*Bar, "init")); -} - -TEST_F(SpecialCaseListTest, AliasIsIn) { - Module M("hello", Ctx); - Function *Foo = makeFunction("foo", M); - GlobalVariable *Bar = makeGlobal("bar", "t", M); - GlobalAlias *FooAlias = makeAlias("fooalias", Foo); - GlobalAlias *BarAlias = makeAlias("baralias", Bar); - - std::unique_ptr SCL(makeSpecialCaseList("fun:foo\n")); - EXPECT_FALSE(SCL->isIn(*FooAlias)); - EXPECT_FALSE(SCL->isIn(*BarAlias)); - - SCL.reset(makeSpecialCaseList("global:bar\n")); - EXPECT_FALSE(SCL->isIn(*FooAlias)); - EXPECT_FALSE(SCL->isIn(*BarAlias)); - - SCL.reset(makeSpecialCaseList("global:fooalias\n")); - EXPECT_FALSE(SCL->isIn(*FooAlias)); - EXPECT_FALSE(SCL->isIn(*BarAlias)); - - SCL.reset(makeSpecialCaseList("fun:fooalias\n")); - EXPECT_TRUE(SCL->isIn(*FooAlias)); - EXPECT_FALSE(SCL->isIn(*BarAlias)); - - SCL.reset(makeSpecialCaseList("global:baralias=init\n")); - EXPECT_FALSE(SCL->isIn(*FooAlias, "init")); - EXPECT_TRUE(SCL->isIn(*BarAlias, "init")); - - SCL.reset(makeSpecialCaseList("type:t=init\n")); - EXPECT_FALSE(SCL->isIn(*FooAlias, "init")); - EXPECT_TRUE(SCL->isIn(*BarAlias, "init")); - - SCL.reset(makeSpecialCaseList("fun:baralias=init\n")); - EXPECT_FALSE(SCL->isIn(*FooAlias, "init")); - EXPECT_FALSE(SCL->isIn(*BarAlias, "init")); -} - -TEST_F(SpecialCaseListTest, Substring) { - Module M("othello", Ctx); - Function *F = makeFunction("tomfoolery", M); - GlobalVariable *GV = makeGlobal("bartender", "t", M); - GlobalAlias *GA1 = makeAlias("buffoonery", F); - GlobalAlias *GA2 = makeAlias("foobar", GV); - - std::unique_ptr SCL(makeSpecialCaseList("src:hello\n" - "fun:foo\n" - "global:bar\n")); - EXPECT_FALSE(SCL->isIn(M)); - EXPECT_FALSE(SCL->isIn(*F)); - EXPECT_FALSE(SCL->isIn(*GV)); - EXPECT_FALSE(SCL->isIn(*GA1)); - EXPECT_FALSE(SCL->isIn(*GA2)); - - SCL.reset(makeSpecialCaseList("fun:*foo*\n")); - EXPECT_TRUE(SCL->isIn(*F)); - EXPECT_TRUE(SCL->isIn(*GA1)); -} - -TEST_F(SpecialCaseListTest, InvalidSpecialCaseList) { - std::string Error; - EXPECT_EQ(nullptr, makeSpecialCaseList("badline", Error)); - EXPECT_EQ("Malformed line 1: 'badline'", Error); - EXPECT_EQ(nullptr, makeSpecialCaseList("src:bad[a-", Error)); - EXPECT_EQ("Malformed regex in line 1: 'bad[a-': invalid character range", - Error); - EXPECT_EQ(nullptr, makeSpecialCaseList("src:a.c\n" - "fun:fun(a\n", - Error)); - EXPECT_EQ("Malformed regex in line 2: 'fun(a': parentheses not balanced", - Error); - EXPECT_EQ(nullptr, SpecialCaseList::create("unexisting", Error)); - EXPECT_EQ(0U, Error.find("Can't open file 'unexisting':")); -} - -TEST_F(SpecialCaseListTest, EmptySpecialCaseList) { - std::unique_ptr SCL(makeSpecialCaseList("")); - Module M("foo", Ctx); - EXPECT_FALSE(SCL->isIn(M)); -} - -}