From: Alexey Samsonov Date: Wed, 9 Jul 2014 19:40:08 +0000 (+0000) Subject: Decouple llvm::SpecialCaseList text representation and its LLVM IR semantics. X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=3e51f754ad08cc453c5e7476bcc6e92743b68830;p=oota-llvm.git 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 --- 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/Support/SpecialCaseList.cpp b/lib/Support/SpecialCaseList.cpp new file mode 100644 index 00000000000..d9921ac6920 --- /dev/null +++ b/lib/Support/SpecialCaseList.cpp @@ -0,0 +1,178 @@ +//===-- SpecialCaseList.cpp - special case list for sanitizers ------------===// +// +// 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, or to instrument some functions or global variables in a specific +// way, based on a user-supplied list. +// +//===----------------------------------------------------------------------===// + +#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/Support/MemoryBuffer.h" +#include "llvm/Support/Regex.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include + +namespace llvm { + +/// Represents a set of regular expressions. Regular expressions which are +/// "literal" (i.e. no regex metacharacters) are stored in Strings, while all +/// others are represented as a single pipe-separated regex in RegEx. The +/// reason for doing so is efficiency; StringSet is much faster at matching +/// literal strings than Regex. +struct SpecialCaseList::Entry { + StringSet<> Strings; + Regex *RegEx; + + Entry() : RegEx(nullptr) {} + + bool match(StringRef Query) const { + return Strings.count(Query) || (RegEx && RegEx->match(Query)); + } +}; + +SpecialCaseList::SpecialCaseList() : Entries() {} + +SpecialCaseList *SpecialCaseList::create( + const StringRef Path, std::string &Error) { + if (Path.empty()) + return new SpecialCaseList(); + ErrorOr> FileOrErr = + MemoryBuffer::getFile(Path); + if (std::error_code EC = FileOrErr.getError()) { + Error = (Twine("Can't open file '") + Path + "': " + EC.message()).str(); + return nullptr; + } + return create(FileOrErr.get().get(), Error); +} + +SpecialCaseList *SpecialCaseList::create( + const MemoryBuffer *MB, std::string &Error) { + std::unique_ptr SCL(new SpecialCaseList()); + if (!SCL->parse(MB, Error)) + return nullptr; + return SCL.release(); +} + +SpecialCaseList *SpecialCaseList::createOrDie(const StringRef Path) { + std::string Error; + if (SpecialCaseList *SCL = create(Path, Error)) + return SCL; + report_fatal_error(Error); +} + +bool SpecialCaseList::parse(const MemoryBuffer *MB, std::string &Error) { + // Iterate through each line in the blacklist file. + SmallVector Lines; + SplitString(MB->getBuffer(), Lines, "\n\r"); + StringMap > Regexps; + assert(Entries.empty() && + "parse() should be called on an empty SpecialCaseList"); + int LineNo = 1; + for (SmallVectorImpl::iterator I = Lines.begin(), E = Lines.end(); + I != E; ++I, ++LineNo) { + // Ignore empty lines and lines starting with "#" + if (I->empty() || I->startswith("#")) + continue; + // Get our prefix and unparsed regexp. + std::pair SplitLine = I->split(":"); + StringRef Prefix = SplitLine.first; + if (SplitLine.second.empty()) { + // Missing ':' in the line. + Error = (Twine("Malformed line ") + Twine(LineNo) + ": '" + + SplitLine.first + "'").str(); + return false; + } + + std::pair SplitRegexp = SplitLine.second.split("="); + std::string Regexp = SplitRegexp.first; + StringRef Category = SplitRegexp.second; + + // Backwards compatibility. + if (Prefix == "global-init") { + Prefix = "global"; + Category = "init"; + } else if (Prefix == "global-init-type") { + Prefix = "type"; + Category = "init"; + } else if (Prefix == "global-init-src") { + Prefix = "src"; + Category = "init"; + } + + // See if we can store Regexp in Strings. + if (Regex::isLiteralERE(Regexp)) { + Entries[Prefix][Category].Strings.insert(Regexp); + continue; + } + + // Replace * with .* + for (size_t pos = 0; (pos = Regexp.find("*", pos)) != std::string::npos; + pos += strlen(".*")) { + Regexp.replace(pos, strlen("*"), ".*"); + } + + // Check that the regexp is valid. + Regex CheckRE(Regexp); + std::string REError; + if (!CheckRE.isValid(REError)) { + Error = (Twine("Malformed regex in line ") + Twine(LineNo) + ": '" + + SplitLine.second + "': " + REError).str(); + return false; + } + + // Add this regexp into the proper group by its prefix. + if (!Regexps[Prefix][Category].empty()) + Regexps[Prefix][Category] += "|"; + Regexps[Prefix][Category] += "^" + Regexp + "$"; + } + + // Iterate through each of the prefixes, and create Regexs for them. + for (StringMap >::const_iterator I = Regexps.begin(), + E = Regexps.end(); + I != E; ++I) { + for (StringMap::const_iterator II = I->second.begin(), + IE = I->second.end(); + II != IE; ++II) { + Entries[I->getKey()][II->getKey()].RegEx = new Regex(II->getValue()); + } + } + return true; +} + +SpecialCaseList::~SpecialCaseList() { + for (StringMap >::iterator I = Entries.begin(), + E = Entries.end(); + I != E; ++I) { + for (StringMap::const_iterator II = I->second.begin(), + IE = I->second.end(); + II != IE; ++II) { + delete II->second.RegEx; + } + } +} + +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); + if (II == I->second.end()) return false; + + return II->getValue().match(Query); +} + +} // namespace llvm 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/lib/Transforms/Utils/SpecialCaseList.cpp b/lib/Transforms/Utils/SpecialCaseList.cpp deleted file mode 100644 index 3d76a17abd9..00000000000 --- a/lib/Transforms/Utils/SpecialCaseList.cpp +++ /dev/null @@ -1,222 +0,0 @@ -//===-- SpecialCaseList.cpp - special case list for sanitizers ------------===// -// -// 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, or to instrument some functions or global variables in a specific -// way, based on a user-supplied list. -// -//===----------------------------------------------------------------------===// - -#include "llvm/Transforms/Utils/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" -#include -#include -#include - -namespace llvm { - -/// Represents a set of regular expressions. Regular expressions which are -/// "literal" (i.e. no regex metacharacters) are stored in Strings, while all -/// others are represented as a single pipe-separated regex in RegEx. The -/// reason for doing so is efficiency; StringSet is much faster at matching -/// literal strings than Regex. -struct SpecialCaseList::Entry { - StringSet<> Strings; - Regex *RegEx; - - Entry() : RegEx(nullptr) {} - - bool match(StringRef Query) const { - return Strings.count(Query) || (RegEx && RegEx->match(Query)); - } -}; - -SpecialCaseList::SpecialCaseList() : Entries() {} - -SpecialCaseList *SpecialCaseList::create( - const StringRef Path, std::string &Error) { - if (Path.empty()) - return new SpecialCaseList(); - ErrorOr> FileOrErr = - MemoryBuffer::getFile(Path); - if (std::error_code EC = FileOrErr.getError()) { - Error = (Twine("Can't open file '") + Path + "': " + EC.message()).str(); - return nullptr; - } - return create(FileOrErr.get().get(), Error); -} - -SpecialCaseList *SpecialCaseList::create( - const MemoryBuffer *MB, std::string &Error) { - std::unique_ptr SCL(new SpecialCaseList()); - if (!SCL->parse(MB, Error)) - return nullptr; - return SCL.release(); -} - -SpecialCaseList *SpecialCaseList::createOrDie(const StringRef Path) { - std::string Error; - if (SpecialCaseList *SCL = create(Path, Error)) - return SCL; - report_fatal_error(Error); -} - -bool SpecialCaseList::parse(const MemoryBuffer *MB, std::string &Error) { - // Iterate through each line in the blacklist file. - SmallVector Lines; - SplitString(MB->getBuffer(), Lines, "\n\r"); - StringMap > Regexps; - assert(Entries.empty() && - "parse() should be called on an empty SpecialCaseList"); - int LineNo = 1; - for (SmallVectorImpl::iterator I = Lines.begin(), E = Lines.end(); - I != E; ++I, ++LineNo) { - // Ignore empty lines and lines starting with "#" - if (I->empty() || I->startswith("#")) - continue; - // Get our prefix and unparsed regexp. - std::pair SplitLine = I->split(":"); - StringRef Prefix = SplitLine.first; - if (SplitLine.second.empty()) { - // Missing ':' in the line. - Error = (Twine("Malformed line ") + Twine(LineNo) + ": '" + - SplitLine.first + "'").str(); - return false; - } - - std::pair SplitRegexp = SplitLine.second.split("="); - std::string Regexp = SplitRegexp.first; - StringRef Category = SplitRegexp.second; - - // Backwards compatibility. - if (Prefix == "global-init") { - Prefix = "global"; - Category = "init"; - } else if (Prefix == "global-init-type") { - Prefix = "type"; - Category = "init"; - } else if (Prefix == "global-init-src") { - Prefix = "src"; - Category = "init"; - } - - // See if we can store Regexp in Strings. - if (Regex::isLiteralERE(Regexp)) { - Entries[Prefix][Category].Strings.insert(Regexp); - continue; - } - - // Replace * with .* - for (size_t pos = 0; (pos = Regexp.find("*", pos)) != std::string::npos; - pos += strlen(".*")) { - Regexp.replace(pos, strlen("*"), ".*"); - } - - // Check that the regexp is valid. - Regex CheckRE(Regexp); - std::string REError; - if (!CheckRE.isValid(REError)) { - Error = (Twine("Malformed regex in line ") + Twine(LineNo) + ": '" + - SplitLine.second + "': " + REError).str(); - return false; - } - - // Add this regexp into the proper group by its prefix. - if (!Regexps[Prefix][Category].empty()) - Regexps[Prefix][Category] += "|"; - Regexps[Prefix][Category] += "^" + Regexp + "$"; - } - - // Iterate through each of the prefixes, and create Regexs for them. - for (StringMap >::const_iterator I = Regexps.begin(), - E = Regexps.end(); - I != E; ++I) { - for (StringMap::const_iterator II = I->second.begin(), - IE = I->second.end(); - II != IE; ++II) { - Entries[I->getKey()][II->getKey()].RegEx = new Regex(II->getValue()); - } - } - return true; -} - -SpecialCaseList::~SpecialCaseList() { - for (StringMap >::iterator I = Entries.begin(), - E = Entries.end(); - I != E; ++I) { - for (StringMap::const_iterator II = I->second.begin(), - IE = I->second.end(); - II != IE; ++II) { - delete II->second.RegEx; - } - } -} - -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 { - StringMap >::const_iterator I = Entries.find(Section); - if (I == Entries.end()) return false; - StringMap::const_iterator II = I->second.find(Category); - if (II == I->second.end()) return false; - - return II->getValue().match(Query); -} - -} // namespace llvm 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)); -} - -}