diff options
Diffstat (limited to 'utils')
| -rw-r--r-- | utils/ClangDataFormat.py | 113 | ||||
| -rw-r--r-- | utils/TableGen/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | utils/TableGen/ClangAttrEmitter.cpp | 4 | ||||
| -rw-r--r-- | utils/TableGen/ClangCommentCommandInfoEmitter.cpp | 72 | ||||
| -rw-r--r-- | utils/TableGen/ClangCommentHTMLTagsEmitter.cpp | 69 | ||||
| -rw-r--r-- | utils/TableGen/ClangDiagnosticsEmitter.cpp | 20 | ||||
| -rw-r--r-- | utils/TableGen/ClangSACheckersEmitter.cpp | 23 | ||||
| -rw-r--r-- | utils/TableGen/Makefile | 2 | ||||
| -rw-r--r-- | utils/TableGen/NeonEmitter.cpp | 28 | ||||
| -rw-r--r-- | utils/TableGen/OptParserEmitter.cpp | 101 | ||||
| -rw-r--r-- | utils/TableGen/TableGen.cpp | 175 | ||||
| -rw-r--r-- | utils/TableGen/TableGenBackends.h | 5 | ||||
| -rwxr-xr-x | utils/analyzer/CmpRuns.py | 92 | ||||
| -rw-r--r-- | utils/analyzer/SATestAdd.py | 18 | ||||
| -rw-r--r-- | utils/analyzer/SATestBuild.py | 153 | ||||
| -rw-r--r-- | utils/analyzer/SumTimerInfo.py | 17 | ||||
| -rwxr-xr-x | utils/analyzer/update_plist_test.pl | 51 |
17 files changed, 736 insertions, 211 deletions
diff --git a/utils/ClangDataFormat.py b/utils/ClangDataFormat.py new file mode 100644 index 000000000000..ec44d2a31b0f --- /dev/null +++ b/utils/ClangDataFormat.py @@ -0,0 +1,113 @@ +"""lldb data formatters for clang classes. + +Usage +-- +import this file in your ~/.lldbinit by adding this line: + +command script import /path/to/ClangDataFormat.py + +After that, instead of getting this: + +(lldb) p Tok.Loc +(clang::SourceLocation) $0 = { + (unsigned int) ID = 123582 +} + +you'll get: + +(lldb) p Tok.Loc +(clang::SourceLocation) $4 = "/usr/include/i386/_types.h:37:1" (offset: 123582, file) +""" + +import lldb + +def __lldb_init_module(debugger, internal_dict): + debugger.HandleCommand("type summary add -F ClangDataFormat.SourceLocation_summary clang::SourceLocation") + +def SourceLocation_summary(srcloc, internal_dict): + return SourceLocation(srcloc).summary() + +class SourceLocation(object): + def __init__(self, srcloc): + self.srcloc = srcloc + + def offset(self): + return getValueFromExpression(self.srcloc, ".getOffset()").GetValueAsUnsigned() + + def isMacro(self): + return getValueFromExpression(self.srcloc, ".isMacroID()").GetValueAsUnsigned() + + def getPrint(self, srcmgr_path): + print_str = getValueFromExpression(self.srcloc, ".printToString(%s)" % srcmgr_path) + return print_str.GetSummary() + + def summary(self): + desc = "(offset: %d, %s)" % (self.offset(), "macro" if self.isMacro() else "file") + srcmgr_path = findObjectExpressionPath("clang::SourceManager", lldb.frame) + if srcmgr_path: + desc = self.getPrint(srcmgr_path) + " " + desc + return desc + +# Key is a (function address, type name) tuple, value is the expression path for +# an object with such a type name from inside that function. +FramePathMapCache = {} + +def findObjectExpressionPath(typename, frame): + func_addr = frame.GetFunction().GetStartAddress().GetFileAddress() + key = (func_addr, typename) + try: + return FramePathMapCache[key] + except KeyError: + #print "CACHE MISS" + path = None + obj = findObject(typename, frame) + if obj: + path = getExpressionPath(obj) + FramePathMapCache[key] = path + return path + +def findObject(typename, frame): + def getTypename(value): + # FIXME: lldb should provide something like getBaseType + ty = value.GetType() + if ty.IsPointerType() or ty.IsReferenceType(): + return ty.GetPointeeType().GetName() + return ty.GetName() + + def searchForType(value, searched): + tyname = getTypename(value) + #print "SEARCH:", getExpressionPath(value), value.GetType().GetName() + if tyname == typename: + return value + ty = value.GetType() + if not (ty.IsPointerType() or + ty.IsReferenceType() or + # FIXME: lldb should provide something like getCanonicalType + tyname.startswith("llvm::IntrusiveRefCntPtr<") or + tyname.startswith("llvm::OwningPtr<")): + return None + # FIXME: Hashing for SBTypes does not seem to work correctly, uses the typename instead, + # and not the canonical one unfortunately. + if tyname in searched: + return None + searched.add(tyname) + for i in range(value.GetNumChildren()): + child = value.GetChildAtIndex(i, 0, False) + found = searchForType(child, searched) + if found: + return found + + searched = set() + value_list = frame.GetVariables(True, True, True, True) + for val in value_list: + found = searchForType(val, searched) + if found: + return found if not found.TypeIsPointerType() else found.Dereference() + +def getValueFromExpression(val, expr): + return lldb.frame.EvaluateExpression(getExpressionPath(val) + expr) + +def getExpressionPath(val): + stream = lldb.SBStream() + val.GetExpressionPath(stream) + return stream.GetData() diff --git a/utils/TableGen/CMakeLists.txt b/utils/TableGen/CMakeLists.txt index 0d879214d4a2..534ac9af7760 100644 --- a/utils/TableGen/CMakeLists.txt +++ b/utils/TableGen/CMakeLists.txt @@ -1,10 +1,10 @@ -set(LLVM_REQUIRES_EH 1) -set(LLVM_REQUIRES_RTTI 1) set(LLVM_LINK_COMPONENTS Support) add_tablegen(clang-tblgen CLANG ClangASTNodesEmitter.cpp ClangAttrEmitter.cpp + ClangCommentCommandInfoEmitter.cpp + ClangCommentHTMLTagsEmitter.cpp ClangDiagnosticsEmitter.cpp ClangSACheckersEmitter.cpp NeonEmitter.cpp diff --git a/utils/TableGen/ClangAttrEmitter.cpp b/utils/TableGen/ClangAttrEmitter.cpp index ef1ad3e1d2d9..521f6046cfa2 100644 --- a/utils/TableGen/ClangAttrEmitter.cpp +++ b/utils/TableGen/ClangAttrEmitter.cpp @@ -33,7 +33,7 @@ getValueAsListOfStrings(Record &R, StringRef FieldName) { i != e; ++i) { assert(*i && "Got a null element in a ListInit"); - if (StringInit *S = dynamic_cast<StringInit *>(*i)) + if (StringInit *S = dyn_cast<StringInit>(*i)) Strings.push_back(S->getValue()); else assert(false && "Got a non-string, non-code element in a ListInit"); @@ -743,8 +743,6 @@ void EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) { OS << " static bool classof(const Attr *A) { return A->getKind() == " << "attr::" << R.getName() << "; }\n"; - OS << " static bool classof(const " << R.getName() - << "Attr *) { return true; }\n"; bool LateParsed = R.getValueAsBit("LateParsed"); OS << " virtual bool isLateParsed() const { return " diff --git a/utils/TableGen/ClangCommentCommandInfoEmitter.cpp b/utils/TableGen/ClangCommentCommandInfoEmitter.cpp new file mode 100644 index 000000000000..36fbcd40b2f4 --- /dev/null +++ b/utils/TableGen/ClangCommentCommandInfoEmitter.cpp @@ -0,0 +1,72 @@ +//===--- ClangCommentCommandInfoEmitter.cpp - Generate command lists -----====// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This tablegen backend emits command lists and efficient matchers command +// names that are used in documentation comments. +// +//===----------------------------------------------------------------------===// + +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/StringMatcher.h" +#include <vector> + +using namespace llvm; + +namespace clang { +void EmitClangCommentCommandInfo(RecordKeeper &Records, raw_ostream &OS) { + OS << "// This file is generated by TableGen. Do not edit.\n\n"; + + OS << "namespace {\n" + "const CommandInfo Commands[] = {\n"; + std::vector<Record *> Tags = Records.getAllDerivedDefinitions("Command"); + for (size_t i = 0, e = Tags.size(); i != e; ++i) { + Record &Tag = *Tags[i]; + OS << " { " + << "\"" << Tag.getValueAsString("Name") << "\", " + << "\"" << Tag.getValueAsString("EndCommandName") << "\", " + << i << ", " + << Tag.getValueAsInt("NumArgs") << ", " + << Tag.getValueAsBit("IsInlineCommand") << ", " + << Tag.getValueAsBit("IsBlockCommand") << ", " + << Tag.getValueAsBit("IsBriefCommand") << ", " + << Tag.getValueAsBit("IsReturnsCommand") << ", " + << Tag.getValueAsBit("IsParamCommand") << ", " + << Tag.getValueAsBit("IsTParamCommand") << ", " + << Tag.getValueAsBit("IsDeprecatedCommand") << ", " + << Tag.getValueAsBit("IsEmptyParagraphAllowed") << ", " + << Tag.getValueAsBit("IsVerbatimBlockCommand") << ", " + << Tag.getValueAsBit("IsVerbatimBlockEndCommand") << ", " + << Tag.getValueAsBit("IsVerbatimLineCommand") << ", " + << Tag.getValueAsBit("IsDeclarationCommand") << ", " + << /* IsUnknownCommand = */ "0" + << " }"; + if (i + 1 != e) + OS << ","; + OS << "\n"; + } + OS << "};\n" + "} // unnamed namespace\n\n"; + + std::vector<StringMatcher::StringPair> Matches; + for (size_t i = 0, e = Tags.size(); i != e; ++i) { + Record &Tag = *Tags[i]; + std::string Name = Tag.getValueAsString("Name"); + std::string Return; + raw_string_ostream(Return) << "return &Commands[" << i << "];"; + Matches.push_back(StringMatcher::StringPair(Name, Return)); + } + + OS << "const CommandInfo *CommandTraits::getBuiltinCommandInfo(\n" + << " StringRef Name) {\n"; + StringMatcher("Name", Matches, OS).Emit(); + OS << " return NULL;\n" + << "}\n\n"; +} +} // end namespace clang + diff --git a/utils/TableGen/ClangCommentHTMLTagsEmitter.cpp b/utils/TableGen/ClangCommentHTMLTagsEmitter.cpp new file mode 100644 index 000000000000..0ae23b293e65 --- /dev/null +++ b/utils/TableGen/ClangCommentHTMLTagsEmitter.cpp @@ -0,0 +1,69 @@ +//===--- ClangCommentHTMLTagsEmitter.cpp - Generate HTML tag list for Clang -=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This tablegen backend emits efficient matchers for HTML tags that are used +// in documentation comments. +// +//===----------------------------------------------------------------------===// + +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/StringMatcher.h" +#include <vector> + +using namespace llvm; + +namespace clang { +void EmitClangCommentHTMLTags(RecordKeeper &Records, raw_ostream &OS) { + std::vector<Record *> Tags = Records.getAllDerivedDefinitions("Tag"); + std::vector<StringMatcher::StringPair> Matches; + for (std::vector<Record *>::iterator I = Tags.begin(), E = Tags.end(); + I != E; ++I) { + Record &Tag = **I; + std::string Spelling = Tag.getValueAsString("Spelling"); + Matches.push_back(StringMatcher::StringPair(Spelling, "return true;")); + } + + OS << "// This file is generated by TableGen. Do not edit.\n\n"; + + OS << "bool isHTMLTagName(StringRef Name) {\n"; + StringMatcher("Name", Matches, OS).Emit(); + OS << " return false;\n" + << "}\n\n"; +} + +void EmitClangCommentHTMLTagsProperties(RecordKeeper &Records, + raw_ostream &OS) { + std::vector<Record *> Tags = Records.getAllDerivedDefinitions("Tag"); + std::vector<StringMatcher::StringPair> MatchesEndTagOptional; + std::vector<StringMatcher::StringPair> MatchesEndTagForbidden; + for (std::vector<Record *>::iterator I = Tags.begin(), E = Tags.end(); + I != E; ++I) { + Record &Tag = **I; + std::string Spelling = Tag.getValueAsString("Spelling"); + StringMatcher::StringPair Match(Spelling, "return true;"); + if (Tag.getValueAsBit("EndTagOptional")) + MatchesEndTagOptional.push_back(Match); + if (Tag.getValueAsBit("EndTagForbidden")) + MatchesEndTagForbidden.push_back(Match); + } + + OS << "// This file is generated by TableGen. Do not edit.\n\n"; + + OS << "bool isHTMLEndTagOptional(StringRef Name) {\n"; + StringMatcher("Name", MatchesEndTagOptional, OS).Emit(); + OS << " return false;\n" + << "}\n\n"; + + OS << "bool isHTMLEndTagForbidden(StringRef Name) {\n"; + StringMatcher("Name", MatchesEndTagForbidden, OS).Emit(); + OS << " return false;\n" + << "}\n\n"; +} +} // end namespace clang + diff --git a/utils/TableGen/ClangDiagnosticsEmitter.cpp b/utils/TableGen/ClangDiagnosticsEmitter.cpp index 8615d2db8cda..b1472a87cc10 100644 --- a/utils/TableGen/ClangDiagnosticsEmitter.cpp +++ b/utils/TableGen/ClangDiagnosticsEmitter.cpp @@ -18,6 +18,7 @@ #include "llvm/ADT/Optional.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Debug.h" +#include "llvm/TableGen/Error.h" #include "llvm/TableGen/Record.h" #include "llvm/TableGen/TableGenBackend.h" #include <algorithm> @@ -75,7 +76,7 @@ getCategoryFromDiagGroup(const Record *Group, static std::string getDiagnosticCategory(const Record *R, DiagGroupParentMap &DiagGroupParents) { // If the diagnostic is in a group, and that group has a category, use it. - if (DefInit *Group = dynamic_cast<DefInit*>(R->getValueInit("Group"))) { + if (DefInit *Group = dyn_cast<DefInit>(R->getValueInit("Group"))) { // Check the diagnostic's diag group for a category. std::string CatName = getCategoryFromDiagGroup(Group->getDef(), DiagGroupParents); @@ -136,7 +137,7 @@ static void groupDiagnostics(const std::vector<Record*> &Diags, std::map<std::string, GroupInfo> &DiagsInGroup) { for (unsigned i = 0, e = Diags.size(); i != e; ++i) { const Record *R = Diags[i]; - DefInit *DI = dynamic_cast<DefInit*>(R->getValueInit("Group")); + DefInit *DI = dyn_cast<DefInit>(R->getValueInit("Group")); if (DI == 0) continue; assert(R->getValueAsDef("Class")->getName() != "CLASS_NOTE" && "Note can't be in a DiagGroup"); @@ -280,7 +281,7 @@ void InferPedantic::compute(VecOrSet DiagsInPedantic, Record *R = Diags[i]; if (isExtension(R) && isOffByDefault(R)) { DiagsSet.insert(R); - if (DefInit *Group = dynamic_cast<DefInit*>(R->getValueInit("Group"))) { + if (DefInit *Group = dyn_cast<DefInit>(R->getValueInit("Group"))) { const Record *GroupRec = Group->getDef(); if (!isSubGroupOfGroup(GroupRec, "pedantic")) { markGroup(GroupRec); @@ -299,7 +300,7 @@ void InferPedantic::compute(VecOrSet DiagsInPedantic, // Check if the group is implicitly in -Wpedantic. If so, // the diagnostic should not be directly included in the -Wpedantic // diagnostic group. - if (DefInit *Group = dynamic_cast<DefInit*>(R->getValueInit("Group"))) + if (DefInit *Group = dyn_cast<DefInit>(R->getValueInit("Group"))) if (groupInPedantic(Group->getDef())) continue; @@ -391,11 +392,11 @@ void EmitClangDiagsDefs(RecordKeeper &Records, raw_ostream &OS, // Check if this is an error that is accidentally in a warning // group. if (isError(R)) { - if (DefInit *Group = dynamic_cast<DefInit*>(R.getValueInit("Group"))) { + if (DefInit *Group = dyn_cast<DefInit>(R.getValueInit("Group"))) { const Record *GroupRec = Group->getDef(); const std::string &GroupName = GroupRec->getValueAsString("GroupName"); - throw "Error " + R.getName() + " cannot be in a warning group [" + - GroupName + "]"; + PrintFatalError(R.getLoc(), "Error " + R.getName() + + " cannot be in a warning group [" + GroupName + "]"); } } @@ -413,7 +414,7 @@ void EmitClangDiagsDefs(RecordKeeper &Records, raw_ostream &OS, // Warning associated with the diagnostic. This is stored as an index into // the alphabetically sorted warning table. - if (DefInit *DI = dynamic_cast<DefInit*>(R.getValueInit("Group"))) { + if (DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Group"))) { std::map<std::string, GroupInfo>::iterator I = DiagsInGroup.find(DI->getDef()->getValueAsString("GroupName")); assert(I != DiagsInGroup.end()); @@ -556,7 +557,8 @@ void EmitClangDiagGroups(RecordKeeper &Records, raw_ostream &OS) { if (I->first.find_first_not_of("abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789!@#$%^*-+=:?")!=std::string::npos) - throw "Invalid character in diagnostic group '" + I->first + "'"; + PrintFatalError("Invalid character in diagnostic group '" + + I->first + "'"); OS.write_escaped(I->first) << "\"," << std::string(MaxLen-I->first.size()+1, ' '); diff --git a/utils/TableGen/ClangSACheckersEmitter.cpp b/utils/TableGen/ClangSACheckersEmitter.cpp index 5a0db501dff9..8c74064a6368 100644 --- a/utils/TableGen/ClangSACheckersEmitter.cpp +++ b/utils/TableGen/ClangSACheckersEmitter.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "llvm/ADT/DenseSet.h" +#include "llvm/TableGen/Error.h" #include "llvm/TableGen/Record.h" #include "llvm/TableGen/TableGenBackend.h" #include <map> @@ -28,7 +29,7 @@ static bool isHidden(const Record &R) { if (R.getValueAsBit("Hidden")) return true; // Not declared as hidden, check the parent package if it is hidden. - if (DefInit *DI = dynamic_cast<DefInit*>(R.getValueInit("ParentPackage"))) + if (DefInit *DI = dyn_cast<DefInit>(R.getValueInit("ParentPackage"))) return isHidden(*DI->getDef()); return false; @@ -42,7 +43,7 @@ static std::string getPackageFullName(const Record *R); static std::string getParentPackageFullName(const Record *R) { std::string name; - if (DefInit *DI = dynamic_cast<DefInit*>(R->getValueInit("ParentPackage"))) + if (DefInit *DI = dyn_cast<DefInit>(R->getValueInit("ParentPackage"))) name = getPackageFullName(DI->getDef()); return name; } @@ -63,8 +64,7 @@ static std::string getCheckerFullName(const Record *R) { } static std::string getStringValue(const Record &R, StringRef field) { - if (StringInit * - SI = dynamic_cast<StringInit*>(R.getValueInit(field))) + if (StringInit *SI = dyn_cast<StringInit>(R.getValueInit(field))) return SI->getValue(); return std::string(); } @@ -131,10 +131,11 @@ void EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) { Record *R = checkers[i]; Record *package = 0; if (DefInit * - DI = dynamic_cast<DefInit*>(R->getValueInit("ParentPackage"))) + DI = dyn_cast<DefInit>(R->getValueInit("ParentPackage"))) package = DI->getDef(); if (!isCheckerNamed(R) && !package) - throw "Checker '" + R->getName() + "' is neither named, nor in a package!"; + PrintFatalError(R->getLoc(), "Checker '" + R->getName() + + "' is neither named, nor in a package!"); if (isCheckerNamed(R)) { // Create a pseudo-group to hold this checker. @@ -151,20 +152,20 @@ void EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) { // Insert the checker and its parent packages into the subgroups set of // the corresponding parent package. while (DefInit *DI - = dynamic_cast<DefInit*>(currR->getValueInit("ParentPackage"))) { + = dyn_cast<DefInit>(currR->getValueInit("ParentPackage"))) { Record *parentPackage = DI->getDef(); recordGroupMap[parentPackage]->SubGroups.insert(currR); currR = parentPackage; } // Insert the checker into the set of its group. - if (DefInit *DI = dynamic_cast<DefInit*>(R->getValueInit("Group"))) + if (DefInit *DI = dyn_cast<DefInit>(R->getValueInit("Group"))) recordGroupMap[DI->getDef()]->Checkers.insert(R); } // If a package is in group, add all its checkers and its sub-packages // checkers into the group. for (unsigned i = 0, e = packages.size(); i != e; ++i) - if (DefInit *DI = dynamic_cast<DefInit*>(packages[i]->getValueInit("Group"))) + if (DefInit *DI = dyn_cast<DefInit>(packages[i]->getValueInit("Group"))) addPackageToCheckerGroup(packages[i], DI->getDef(), recordGroupMap); typedef std::map<std::string, const Record *> SortedRecords; @@ -205,7 +206,7 @@ void EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) { OS << "PACKAGE(" << "\""; OS.write_escaped(getPackageFullName(&R)) << "\", "; // Group index - if (DefInit *DI = dynamic_cast<DefInit*>(R.getValueInit("Group"))) + if (DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Group"))) OS << groupToSortIndex[DI->getDef()] << ", "; else OS << "-1, "; @@ -233,7 +234,7 @@ void EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) { OS << "\""; OS.write_escaped(getStringValue(R, "HelpText")) << "\", "; // Group index - if (DefInit *DI = dynamic_cast<DefInit*>(R.getValueInit("Group"))) + if (DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Group"))) OS << groupToSortIndex[DI->getDef()] << ", "; else OS << "-1, "; diff --git a/utils/TableGen/Makefile b/utils/TableGen/Makefile index 9790efc061ef..1fde852ebc47 100644 --- a/utils/TableGen/Makefile +++ b/utils/TableGen/Makefile @@ -10,8 +10,6 @@ LEVEL = ../../../.. TOOLNAME = clang-tblgen USEDLIBS = LLVMTableGen.a LLVMSupport.a -REQUIRES_EH := 1 -REQUIRES_RTTI := 1 # This tool has no plugins, optimize startup time. TOOL_NO_EXPORTS = 1 diff --git a/utils/TableGen/NeonEmitter.cpp b/utils/TableGen/NeonEmitter.cpp index 68373063baae..d453ededd5e9 100644 --- a/utils/TableGen/NeonEmitter.cpp +++ b/utils/TableGen/NeonEmitter.cpp @@ -245,7 +245,7 @@ static void ParseTypes(Record *r, std::string &s, case 'f': break; default: - throw TGError(r->getLoc(), + PrintFatalError(r->getLoc(), "Unexpected letter: " + std::string(data + len, 1)); } TV.push_back(StringRef(data, len + 1)); @@ -266,7 +266,8 @@ static char Widen(const char t) { return 'l'; case 'h': return 'f'; - default: throw "unhandled type in widen!"; + default: + PrintFatalError("unhandled type in widen!"); } } @@ -282,7 +283,8 @@ static char Narrow(const char t) { return 'i'; case 'f': return 'h'; - default: throw "unhandled type in narrow!"; + default: + PrintFatalError("unhandled type in narrow!"); } } @@ -453,7 +455,7 @@ static std::string TypeString(const char mod, StringRef typestr) { s += quad ? "x4" : "x2"; break; default: - throw "unhandled type!"; + PrintFatalError("unhandled type!"); } if (mod == '2') @@ -635,7 +637,7 @@ static std::string MangleName(const std::string &name, StringRef typestr, } break; default: - throw "unhandled type!"; + PrintFatalError("unhandled type!"); } if (ck == ClassB) s += "_v"; @@ -773,7 +775,7 @@ static unsigned GetNumElements(StringRef typestr, bool &quad) { case 'h': nElts = 4; break; case 'f': nElts = 2; break; default: - throw "unhandled type!"; + PrintFatalError("unhandled type!"); } if (quad) nElts <<= 1; return nElts; @@ -1004,7 +1006,7 @@ static std::string GenOpString(OpKind op, const std::string &proto, break; } default: - throw "unknown OpKind!"; + PrintFatalError("unknown OpKind!"); } return s; } @@ -1049,7 +1051,7 @@ static unsigned GetNeonEnum(const std::string &proto, StringRef typestr) { ET = NeonTypeFlags::Float32; break; default: - throw "unhandled type!"; + PrintFatalError("unhandled type!"); } NeonTypeFlags Flags(ET, usgn, quad && proto[1] != 'g'); return Flags.getFlags(); @@ -1381,7 +1383,7 @@ void NeonEmitter::emitIntrinsic(raw_ostream &OS, Record *R) { if (R->getSuperClasses().size() >= 2) classKind = ClassMap[R->getSuperClasses()[1]]; if (classKind == ClassNone && kind == OpNone) - throw TGError(R->getLoc(), "Builtin has no class kind"); + PrintFatalError(R->getLoc(), "Builtin has no class kind"); for (unsigned ti = 0, te = TypeVec.size(); ti != te; ++ti) { if (kind == OpReinterpret) { @@ -1423,7 +1425,7 @@ static unsigned RangeFromType(const char mod, StringRef typestr) { case 'l': return (1 << (int)quad) - 1; default: - throw "unhandled type!"; + PrintFatalError("unhandled type!"); } } @@ -1456,7 +1458,7 @@ void NeonEmitter::runHeader(raw_ostream &OS) { ParseTypes(R, Types, TypeVec); if (R->getSuperClasses().size() < 2) - throw TGError(R->getLoc(), "Builtin has no class kind"); + PrintFatalError(R->getLoc(), "Builtin has no class kind"); std::string name = R->getValueAsString("Name"); ClassKind ck = ClassMap[R->getSuperClasses()[1]]; @@ -1501,7 +1503,7 @@ void NeonEmitter::runHeader(raw_ostream &OS) { ParseTypes(R, Types, TypeVec); if (R->getSuperClasses().size() < 2) - throw TGError(R->getLoc(), "Builtin has no class kind"); + PrintFatalError(R->getLoc(), "Builtin has no class kind"); int si = -1, qi = -1; uint64_t mask = 0, qmask = 0; @@ -1600,7 +1602,7 @@ void NeonEmitter::runHeader(raw_ostream &OS) { ParseTypes(R, Types, TypeVec); if (R->getSuperClasses().size() < 2) - throw TGError(R->getLoc(), "Builtin has no class kind"); + PrintFatalError(R->getLoc(), "Builtin has no class kind"); ClassKind ck = ClassMap[R->getSuperClasses()[1]]; diff --git a/utils/TableGen/OptParserEmitter.cpp b/utils/TableGen/OptParserEmitter.cpp index b0431a9be16b..674c89af9f99 100644 --- a/utils/TableGen/OptParserEmitter.cpp +++ b/utils/TableGen/OptParserEmitter.cpp @@ -7,9 +7,15 @@ // //===----------------------------------------------------------------------===// +#include "llvm/TableGen/Error.h" #include "llvm/TableGen/Record.h" #include "llvm/TableGen/TableGenBackend.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/Twine.h" + +#include <map> + using namespace llvm; static int StrCmpOptionName(const char *A, const char *B) { @@ -32,8 +38,8 @@ static int StrCmpOptionName(const char *A, const char *B) { } static int CompareOptionRecords(const void *Av, const void *Bv) { - const Record *A = *(Record**) Av; - const Record *B = *(Record**) Bv; + const Record *A = *(const Record*const*) Av; + const Record *B = *(const Record*const*) Bv; // Sentinel options precede all others and are only ordered by precedence. bool ASent = A->getValueAsDef("Kind")->getValueAsBit("Sentinel"); @@ -47,16 +53,38 @@ static int CompareOptionRecords(const void *Av, const void *Bv) { B->getValueAsString("Name").c_str())) return Cmp; + if (!ASent) { + std::vector<std::string> APrefixes = A->getValueAsListOfStrings("Prefixes"); + std::vector<std::string> BPrefixes = B->getValueAsListOfStrings("Prefixes"); + + for (std::vector<std::string>::const_iterator APre = APrefixes.begin(), + AEPre = APrefixes.end(), + BPre = BPrefixes.begin(), + BEPre = BPrefixes.end(); + APre != AEPre && + BPre != BEPre; + ++APre, ++BPre) { + if (int Cmp = StrCmpOptionName(APre->c_str(), BPre->c_str())) + return Cmp; + } + } + // Then by the kind precedence; int APrec = A->getValueAsDef("Kind")->getValueAsInt("Precedence"); int BPrec = B->getValueAsDef("Kind")->getValueAsInt("Precedence"); - assert(APrec != BPrec && "Options are equivalent!"); + if (APrec == BPrec && + A->getValueAsListOfStrings("Prefixes") == + B->getValueAsListOfStrings("Prefixes")) { + PrintError(A->getLoc(), Twine("Option is equivilent to")); + PrintError(B->getLoc(), Twine("Other defined here")); + PrintFatalError("Equivalent Options found."); + } return APrec < BPrec ? -1 : 1; } static const std::string getOptionName(const Record &R) { // Use the record name unless EnumName is defined. - if (dynamic_cast<UnsetInit*>(R.getValueInit("EnumName"))) + if (isa<UnsetInit>(R.getValueInit("EnumName"))) return R.getName(); return R.getValueAsString("EnumName"); @@ -86,6 +114,48 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS, bool GenDefs) { array_pod_sort(Opts.begin(), Opts.end(), CompareOptionRecords); if (GenDefs) { + // Generate prefix groups. + typedef SmallVector<SmallString<2>, 2> PrefixKeyT; + typedef std::map<PrefixKeyT, std::string> PrefixesT; + PrefixesT Prefixes; + Prefixes.insert(std::make_pair(PrefixKeyT(), "prefix_0")); + unsigned CurPrefix = 0; + for (unsigned i = 0, e = Opts.size(); i != e; ++i) { + const Record &R = *Opts[i]; + std::vector<std::string> prf = R.getValueAsListOfStrings("Prefixes"); + PrefixKeyT prfkey(prf.begin(), prf.end()); + unsigned NewPrefix = CurPrefix + 1; + if (Prefixes.insert(std::make_pair(prfkey, (Twine("prefix_") + + Twine(NewPrefix)).str())).second) + CurPrefix = NewPrefix; + } + + OS << "#ifndef PREFIX\n"; + OS << "#error \"Define PREFIX prior to including this file!\"\n"; + OS << "#endif\n\n"; + + // Dump prefixes. + OS << "/////////\n"; + OS << "// Prefixes\n\n"; + OS << "#define COMMA ,\n"; + for (PrefixesT::const_iterator I = Prefixes.begin(), E = Prefixes.end(); + I != E; ++I) { + OS << "PREFIX("; + + // Prefix name. + OS << I->second; + + // Prefix values. + OS << ", {"; + for (PrefixKeyT::const_iterator PI = I->first.begin(), + PE = I->first.end(); PI != PE; ++PI) { + OS << "\"" << *PI << "\" COMMA "; + } + OS << "0})\n"; + } + OS << "#undef COMMA\n"; + OS << "\n"; + OS << "#ifndef OPTION\n"; OS << "#error \"Define OPTION prior to including this file!\"\n"; OS << "#endif\n\n"; @@ -98,8 +168,11 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS, bool GenDefs) { // Start a single option entry. OS << "OPTION("; + // The option prefix; + OS << "0"; + // The option string. - OS << '"' << R.getValueAsString("Name") << '"'; + OS << ", \"" << R.getValueAsString("Name") << '"'; // The option identifier name. OS << ", "<< getOptionName(R); @@ -109,7 +182,7 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS, bool GenDefs) { // The containing option group (if any). OS << ", "; - if (const DefInit *DI = dynamic_cast<DefInit*>(R.getValueInit("Group"))) + if (const DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Group"))) OS << getOptionName(*DI->getDef()); else OS << "INVALID"; @@ -118,7 +191,7 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS, bool GenDefs) { OS << ", INVALID, 0, 0"; // The option help text. - if (!dynamic_cast<UnsetInit*>(R.getValueInit("HelpText"))) { + if (!isa<UnsetInit>(R.getValueInit("HelpText"))) { OS << ",\n"; OS << " "; write_cstring(OS, R.getValueAsString("HelpText")); @@ -138,6 +211,10 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS, bool GenDefs) { // Start a single option entry. OS << "OPTION("; + // The option prefix; + std::vector<std::string> prf = R.getValueAsListOfStrings("Prefixes"); + OS << Prefixes[PrefixKeyT(prf.begin(), prf.end())] << ", "; + // The option string. write_cstring(OS, R.getValueAsString("Name")); @@ -149,14 +226,14 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS, bool GenDefs) { // The containing option group (if any). OS << ", "; - if (const DefInit *DI = dynamic_cast<DefInit*>(R.getValueInit("Group"))) + if (const DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Group"))) OS << getOptionName(*DI->getDef()); else OS << "INVALID"; // The option alias (if any). OS << ", "; - if (const DefInit *DI = dynamic_cast<DefInit*>(R.getValueInit("Alias"))) + if (const DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Alias"))) OS << getOptionName(*DI->getDef()); else OS << "INVALID"; @@ -170,7 +247,7 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS, bool GenDefs) { for (unsigned i = 0, e = LI->size(); i != e; ++i) { if (i) OS << " | "; - OS << dynamic_cast<DefInit*>(LI->getElement(i))->getDef()->getName(); + OS << cast<DefInit>(LI->getElement(i))->getDef()->getName(); } } @@ -178,7 +255,7 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS, bool GenDefs) { OS << ", " << R.getValueAsInt("NumArgs"); // The option help text. - if (!dynamic_cast<UnsetInit*>(R.getValueInit("HelpText"))) { + if (!isa<UnsetInit>(R.getValueInit("HelpText"))) { OS << ",\n"; OS << " "; write_cstring(OS, R.getValueAsString("HelpText")); @@ -187,7 +264,7 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS, bool GenDefs) { // The option meta-variable name. OS << ", "; - if (!dynamic_cast<UnsetInit*>(R.getValueInit("MetaVarName"))) + if (!isa<UnsetInit>(R.getValueInit("MetaVarName"))) write_cstring(OS, R.getValueAsString("MetaVarName")); else OS << "0"; diff --git a/utils/TableGen/TableGen.cpp b/utils/TableGen/TableGen.cpp index d3408ed20f4d..41471a484c69 100644 --- a/utils/TableGen/TableGen.cpp +++ b/utils/TableGen/TableGen.cpp @@ -19,7 +19,6 @@ #include "llvm/TableGen/Error.h" #include "llvm/TableGen/Main.h" #include "llvm/TableGen/Record.h" -#include "llvm/TableGen/TableGenAction.h" using namespace llvm; using namespace clang; @@ -42,6 +41,9 @@ enum ActionType { GenClangDeclNodes, GenClangStmtNodes, GenClangSACheckers, + GenClangCommentHTMLTags, + GenClangCommentHTMLTagsProperties, + GenClangCommentCommandInfo, GenOptParserDefs, GenOptParserImpl, GenArmNeon, GenArmNeonSema, @@ -95,6 +97,18 @@ namespace { "Generate Clang AST statement nodes"), clEnumValN(GenClangSACheckers, "gen-clang-sa-checkers", "Generate Clang Static Analyzer checkers"), + clEnumValN(GenClangCommentHTMLTags, + "gen-clang-comment-html-tags", + "Generate efficient matchers for HTML tag " + "names that are used in documentation comments"), + clEnumValN(GenClangCommentHTMLTagsProperties, + "gen-clang-comment-html-tags-properties", + "Generate efficient matchers for HTML tag " + "properties"), + clEnumValN(GenClangCommentCommandInfo, + "gen-clang-comment-command-info", + "Generate list of commands that are used in " + "documentation comments"), clEnumValN(GenArmNeon, "gen-arm-neon", "Generate arm_neon.h for clang"), clEnumValN(GenArmNeonSema, "gen-arm-neon-sema", @@ -108,82 +122,88 @@ namespace { cl::desc("Only use warnings from specified component"), cl::value_desc("component"), cl::Hidden); -class ClangTableGenAction : public TableGenAction { -public: - bool operator()(raw_ostream &OS, RecordKeeper &Records) { - switch (Action) { - case GenClangAttrClasses: - EmitClangAttrClass(Records, OS); - break; - case GenClangAttrImpl: - EmitClangAttrImpl(Records, OS); - break; - case GenClangAttrList: - EmitClangAttrList(Records, OS); - break; - case GenClangAttrPCHRead: - EmitClangAttrPCHRead(Records, OS); - break; - case GenClangAttrPCHWrite: - EmitClangAttrPCHWrite(Records, OS); - break; - case GenClangAttrSpellingList: - EmitClangAttrSpellingList(Records, OS); - break; - case GenClangAttrLateParsedList: - EmitClangAttrLateParsedList(Records, OS); - break; - case GenClangAttrTemplateInstantiate: - EmitClangAttrTemplateInstantiate(Records, OS); - break; - case GenClangAttrParsedAttrList: - EmitClangAttrParsedAttrList(Records, OS); - break; - case GenClangAttrParsedAttrKinds: - EmitClangAttrParsedAttrKinds(Records, OS); - break; - case GenClangDiagsDefs: - EmitClangDiagsDefs(Records, OS, ClangComponent); - break; - case GenClangDiagGroups: - EmitClangDiagGroups(Records, OS); - break; - case GenClangDiagsIndexName: - EmitClangDiagsIndexName(Records, OS); - break; - case GenClangCommentNodes: - EmitClangASTNodes(Records, OS, "Comment", ""); - break; - case GenClangDeclNodes: - EmitClangASTNodes(Records, OS, "Decl", "Decl"); - EmitClangDeclContext(Records, OS); - break; - case GenClangStmtNodes: - EmitClangASTNodes(Records, OS, "Stmt", ""); - break; - case GenClangSACheckers: - EmitClangSACheckers(Records, OS); - break; - case GenOptParserDefs: - EmitOptParser(Records, OS, true); - break; - case GenOptParserImpl: - EmitOptParser(Records, OS, false); - break; - case GenArmNeon: - EmitNeon(Records, OS); - break; - case GenArmNeonSema: - EmitNeonSema(Records, OS); - break; - case GenArmNeonTest: - EmitNeonTest(Records, OS); - break; - } - - return false; +bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) { + switch (Action) { + case GenClangAttrClasses: + EmitClangAttrClass(Records, OS); + break; + case GenClangAttrImpl: + EmitClangAttrImpl(Records, OS); + break; + case GenClangAttrList: + EmitClangAttrList(Records, OS); + break; + case GenClangAttrPCHRead: + EmitClangAttrPCHRead(Records, OS); + break; + case GenClangAttrPCHWrite: + EmitClangAttrPCHWrite(Records, OS); + break; + case GenClangAttrSpellingList: + EmitClangAttrSpellingList(Records, OS); + break; + case GenClangAttrLateParsedList: + EmitClangAttrLateParsedList(Records, OS); + break; + case GenClangAttrTemplateInstantiate: + EmitClangAttrTemplateInstantiate(Records, OS); + break; + case GenClangAttrParsedAttrList: + EmitClangAttrParsedAttrList(Records, OS); + break; + case GenClangAttrParsedAttrKinds: + EmitClangAttrParsedAttrKinds(Records, OS); + break; + case GenClangDiagsDefs: + EmitClangDiagsDefs(Records, OS, ClangComponent); + break; + case GenClangDiagGroups: + EmitClangDiagGroups(Records, OS); + break; + case GenClangDiagsIndexName: + EmitClangDiagsIndexName(Records, OS); + break; + case GenClangCommentNodes: + EmitClangASTNodes(Records, OS, "Comment", ""); + break; + case GenClangDeclNodes: + EmitClangASTNodes(Records, OS, "Decl", "Decl"); + EmitClangDeclContext(Records, OS); + break; + case GenClangStmtNodes: + EmitClangASTNodes(Records, OS, "Stmt", ""); + break; + case GenClangSACheckers: + EmitClangSACheckers(Records, OS); + break; + case GenClangCommentHTMLTags: + EmitClangCommentHTMLTags(Records, OS); + break; + case GenClangCommentHTMLTagsProperties: + EmitClangCommentHTMLTagsProperties(Records, OS); + break; + case GenClangCommentCommandInfo: + EmitClangCommentCommandInfo(Records, OS); + break; + case GenOptParserDefs: + EmitOptParser(Records, OS, true); + break; + case GenOptParserImpl: + EmitOptParser(Records, OS, false); + break; + case GenArmNeon: + EmitNeon(Records, OS); + break; + case GenArmNeonSema: + EmitNeonSema(Records, OS); + break; + case GenArmNeonTest: + EmitNeonTest(Records, OS); + break; } -}; + + return false; +} } int main(int argc, char **argv) { @@ -191,6 +211,5 @@ int main(int argc, char **argv) { PrettyStackTraceProgram X(argc, argv); cl::ParseCommandLineOptions(argc, argv); - ClangTableGenAction Action; - return TableGenMain(argv[0], Action); + return TableGenMain(argv[0], &ClangTableGenMain); } diff --git a/utils/TableGen/TableGenBackends.h b/utils/TableGen/TableGenBackends.h index 779de7c734b0..838fc84dcae3 100644 --- a/utils/TableGen/TableGenBackends.h +++ b/utils/TableGen/TableGenBackends.h @@ -47,6 +47,11 @@ void EmitClangDiagsIndexName(RecordKeeper &Records, raw_ostream &OS); void EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS); +void EmitClangCommentHTMLTags(RecordKeeper &Records, raw_ostream &OS); +void EmitClangCommentHTMLTagsProperties(RecordKeeper &Records, raw_ostream &OS); + +void EmitClangCommentCommandInfo(RecordKeeper &Records, raw_ostream &OS); + void EmitNeon(RecordKeeper &Records, raw_ostream &OS); void EmitNeonSema(RecordKeeper &Records, raw_ostream &OS); void EmitNeonTest(RecordKeeper &Records, raw_ostream &OS); diff --git a/utils/analyzer/CmpRuns.py b/utils/analyzer/CmpRuns.py index c8f05cbcf474..3ca9b2bbe7eb 100755 --- a/utils/analyzer/CmpRuns.py +++ b/utils/analyzer/CmpRuns.py @@ -17,11 +17,8 @@ Usage: # Load the results of both runs, to obtain lists of the corresponding # AnalysisDiagnostic objects. # - # root - the name of the root directory, which will be disregarded when - # determining the source file name - # - resultsA = loadResults(dirA, opts, root, deleteEmpty) - resultsB = loadResults(dirB, opts, root, deleteEmpty) + resultsA = loadResultsFromSingleRun(singleRunInfoA, deleteEmpty) + resultsB = loadResultsFromSingleRun(singleRunInfoB, deleteEmpty) # Generate a relation from diagnostics in run A to diagnostics in run B # to obtain a list of triples (a, b, confidence). @@ -31,8 +28,18 @@ Usage: import os import plistlib +import CmpRuns + +# Information about analysis run: +# path - the analysis output directory +# root - the name of the root directory, which will be disregarded when +# determining the source file name +class SingleRunInfo: + def __init__(self, path, root="", verboseLog=None): + self.path = path + self.root = root + self.verboseLog = verboseLog -# class AnalysisDiagnostic: def __init__(self, data, report, htmlReport): self._data = data @@ -41,7 +48,11 @@ class AnalysisDiagnostic: self._htmlReport = htmlReport def getFileName(self): - return self._report.run.getSourceName(self._report.files[self._loc['file']]) + root = self._report.run.root + fileName = self._report.files[self._loc['file']] + if fileName.startswith(root) : + return fileName[len(root):] + return fileName def getLine(self): return self._loc['line'] @@ -56,12 +67,12 @@ class AnalysisDiagnostic: return self._data['description'] def getIssueIdentifier(self) : - id = '' + id = self.getFileName() + "+" if 'issue_context' in self._data : - id += self._data['issue_context'] + ":" + id += self._data['issue_context'] + "+" if 'issue_hash' in self._data : - id += str(self._data['issue_hash']) + ":" - return id + ":" + self.getFileName() + id += str(self._data['issue_hash']) + return id def getReport(self): if self._htmlReport is None: @@ -72,6 +83,11 @@ class AnalysisDiagnostic: return '%s:%d:%d, %s: %s' % (self.getFileName(), self.getLine(), self.getColumn(), self.getCategory(), self.getDescription()) + + # Note, the data format is not an API and may change from one analyzer + # version to another. + def getRawData(self): + return self._data class multidict: def __init__(self, elts=()): @@ -96,8 +112,6 @@ class multidict: return len(self.data) def get(self, key, default=None): return self.data.get(key, default) - -# class CmpOptions: def __init__(self, verboseLog=None, rootA="", rootB=""): @@ -106,29 +120,36 @@ class CmpOptions: self.verboseLog = verboseLog class AnalysisReport: - def __init__(self, run, files): + def __init__(self, run, files, clang_vers): self.run = run + self.clang_version = clang_vers self.files = files + self.diagnostics = [] class AnalysisRun: - def __init__(self, path, root, opts): - self.path = path - self.root = root + def __init__(self, info): + self.path = info.path + self.root = info.root + self.info = info self.reports = [] + # Cumulative list of all diagnostics from all the reports. self.diagnostics = [] - self.opts = opts - def getSourceName(self, path): - if path.startswith(self.root): - return path[len(self.root):] - return path +# Backward compatibility API. def loadResults(path, opts, root = "", deleteEmpty=True): - run = AnalysisRun(path, root, opts) + return loadResultsFromSingleRun(SingleRunInfo(path, root, opts.verboseLog), + deleteEmpty) + +# Load results of the analyzes from a given output folder. +# - info is the SingleRunInfo object +# - deleteEmpty specifies if the empty plist files should be deleted +def loadResultsFromSingleRun(info, deleteEmpty=True): + path = info.path + run = AnalysisRun(info) for f in os.listdir(path): - if (not f.startswith('report') or - not f.endswith('plist')): + if (not f.endswith('plist')): continue p = os.path.join(path, f) @@ -146,20 +167,23 @@ def loadResults(path, opts, root = "", deleteEmpty=True): for d in data['diagnostics']: # FIXME: Why is this named files, when does it have multiple # files? - # TODO: Add the assert back in after we fix the - # plist-html output. - # assert len(d['HTMLDiagnostics_files']) == 1 + assert len(d['HTMLDiagnostics_files']) == 1 htmlFiles.append(d.pop('HTMLDiagnostics_files')[0]) else: htmlFiles = [None] * len(data['diagnostics']) - - report = AnalysisReport(run, data.pop('files')) + + clang_version = '' + if 'clang_version' in data: + clang_version = data.pop('clang_version') + + report = AnalysisReport(run, data.pop('files'), clang_version) diagnostics = [AnalysisDiagnostic(d, report, h) for d,h in zip(data.pop('diagnostics'), htmlFiles)] assert not data - + + report.diagnostics.extend(diagnostics) run.reports.append(report) run.diagnostics.extend(diagnostics) @@ -193,12 +217,12 @@ def compareResults(A, B): b = eltsB.pop() if (a.getIssueIdentifier() == b.getIssueIdentifier()) : res.append((a, b, 0)) - elif a._data > b._data: - neqA.append(a) + elif a.getIssueIdentifier() > b.getIssueIdentifier(): eltsB.append(b) + neqA.append(a) else: - neqB.append(b) eltsA.append(a) + neqB.append(b) neqA.extend(eltsA) neqB.extend(eltsB) diff --git a/utils/analyzer/SATestAdd.py b/utils/analyzer/SATestAdd.py index 2d32533f6bb0..64ff4ff65683 100644 --- a/utils/analyzer/SATestAdd.py +++ b/utils/analyzer/SATestAdd.py @@ -33,7 +33,7 @@ def isExistingProject(PMapFile, projectID) : # Params: # Dir is the directory where the sources are. # ID is a short string used to identify a project. -def addNewProject(ID, IsScanBuild) : +def addNewProject(ID, BuildMode) : CurDir = os.path.abspath(os.curdir) Dir = SATestBuild.getProjectDir(ID) if not os.path.exists(Dir): @@ -41,7 +41,7 @@ def addNewProject(ID, IsScanBuild) : sys.exit(-1) # Build the project. - SATestBuild.testProject(ID, IsScanBuild, IsReferenceBuild=True, Dir=Dir) + SATestBuild.testProject(ID, BuildMode, IsReferenceBuild=True, Dir=Dir) # Add the project ID to the project map. ProjectMapPath = os.path.join(CurDir, SATestBuild.ProjectMapFile) @@ -57,7 +57,7 @@ def addNewProject(ID, IsScanBuild) : print >> sys.stdout, "Reference output has been regenerated." else: PMapWriter = csv.writer(PMapFile) - PMapWriter.writerow( (ID, int(IsScanBuild)) ); + PMapWriter.writerow( (ID, int(BuildMode)) ); print "The project map is updated: ", ProjectMapPath finally: PMapFile.close() @@ -69,12 +69,14 @@ if __name__ == '__main__': if len(sys.argv) < 2: print >> sys.stderr, 'Usage: ', sys.argv[0],\ 'project_ID <mode>' \ - 'mode - 0 for single file project; 1 for scan_build' + 'mode - 0 for single file project; ' \ + '1 for scan_build; ' \ + '2 for single file c++11 project' sys.exit(-1) - IsScanBuild = 1 + BuildMode = 1 if (len(sys.argv) >= 3): - IsScanBuild = int(sys.argv[2]) - assert((IsScanBuild == 0) | (IsScanBuild == 1)) + BuildMode = int(sys.argv[2]) + assert((BuildMode == 0) | (BuildMode == 1) | (BuildMode == 2)) - addNewProject(sys.argv[1], IsScanBuild) + addNewProject(sys.argv[1], BuildMode) diff --git a/utils/analyzer/SATestBuild.py b/utils/analyzer/SATestBuild.py index fd4bc8a8eeef..94123582225f 100644 --- a/utils/analyzer/SATestBuild.py +++ b/utils/analyzer/SATestBuild.py @@ -42,39 +42,66 @@ import os import csv import sys import glob +import math import shutil import time import plistlib from subprocess import check_call, CalledProcessError -# Project map stores info about all the "registered" projects. -ProjectMapFile = "projectMap.csv" +#------------------------------------------------------------------------------ +# Helper functions. +#------------------------------------------------------------------------------ -# Names of the project specific scripts. -# The script that needs to be executed before the build can start. -CleanupScript = "cleanup_run_static_analyzer.sh" -# This is a file containing commands for scan-build. -BuildScript = "run_static_analyzer.cmd" +def detectCPUs(): + """ + Detects the number of CPUs on a system. Cribbed from pp. + """ + # Linux, Unix and MacOS: + if hasattr(os, "sysconf"): + if os.sysconf_names.has_key("SC_NPROCESSORS_ONLN"): + # Linux & Unix: + ncpus = os.sysconf("SC_NPROCESSORS_ONLN") + if isinstance(ncpus, int) and ncpus > 0: + return ncpus + else: # OSX: + return int(capture(['sysctl', '-n', 'hw.ncpu'])) + # Windows: + if os.environ.has_key("NUMBER_OF_PROCESSORS"): + ncpus = int(os.environ["NUMBER_OF_PROCESSORS"]) + if ncpus > 0: + return ncpus + return 1 # Default -# The log file name. -LogFolderName = "Logs" -BuildLogName = "run_static_analyzer.log" -# Summary file - contains the summary of the failures. Ex: This info can be be -# displayed when buildbot detects a build failure. -NumOfFailuresInSummary = 10 -FailuresSummaryFileName = "failures.txt" -# Summary of the result diffs. -DiffsSummaryFileName = "diffs.txt" +def which(command, paths = None): + """which(command, [paths]) - Look up the given command in the paths string + (or the PATH environment variable, if unspecified).""" -# The scan-build result directory. -SBOutputDirName = "ScanBuildResults" -SBOutputDirReferencePrefix = "Ref" + if paths is None: + paths = os.environ.get('PATH','') -# The list of checkers used during analyzes. -# Currently, consists of all the non experimental checkers. -Checkers="experimental.security.taint,core,deadcode,security,unix,osx" + # Check for absolute match first. + if os.path.exists(command): + return command -Verbose = 1 + # Would be nice if Python had a lib function for this. + if not paths: + paths = os.defpath + + # Get suffixes to search. + # On Cygwin, 'PATHEXT' may exist but it should not be used. + if os.pathsep == ';': + pathext = os.environ.get('PATHEXT', '').split(';') + else: + pathext = [''] + + # Search the paths... + for path in paths.split(os.pathsep): + for ext in pathext: + p = os.path.join(path, command + ext) + if os.path.exists(p): + return p + + return None # Make sure we flush the output after every print statement. class flushfile(object): @@ -104,6 +131,52 @@ def getSBOutputDirName(IsReferenceBuild) : else : return SBOutputDirName +#------------------------------------------------------------------------------ +# Configuration setup. +#------------------------------------------------------------------------------ + +# Find Clang for static analysis. +Clang = which("clang", os.environ['PATH']) +if not Clang: + print "Error: cannot find 'clang' in PATH" + sys.exit(-1) + +# Number of jobs. +Jobs = math.ceil(detectCPUs() * 0.75) + +# Project map stores info about all the "registered" projects. +ProjectMapFile = "projectMap.csv" + +# Names of the project specific scripts. +# The script that needs to be executed before the build can start. +CleanupScript = "cleanup_run_static_analyzer.sh" +# This is a file containing commands for scan-build. +BuildScript = "run_static_analyzer.cmd" + +# The log file name. +LogFolderName = "Logs" +BuildLogName = "run_static_analyzer.log" +# Summary file - contains the summary of the failures. Ex: This info can be be +# displayed when buildbot detects a build failure. +NumOfFailuresInSummary = 10 +FailuresSummaryFileName = "failures.txt" +# Summary of the result diffs. +DiffsSummaryFileName = "diffs.txt" + +# The scan-build result directory. +SBOutputDirName = "ScanBuildResults" +SBOutputDirReferencePrefix = "Ref" + +# The list of checkers used during analyzes. +# Currently, consists of all the non experimental checkers. +Checkers="alpha.unix.SimpleStream,alpha.security.taint,core,deadcode,security,unix,osx" + +Verbose = 1 + +#------------------------------------------------------------------------------ +# Test harness logic. +#------------------------------------------------------------------------------ + # Run pre-processing script if any. def runCleanupScript(Dir, PBuildLogFile): ScriptPath = os.path.join(Dir, CleanupScript) @@ -129,13 +202,19 @@ def runScanBuild(Dir, SBOutputDir, PBuildLogFile): BuildScriptPath = os.path.join(Dir, BuildScript) if not os.path.exists(BuildScriptPath): print "Error: build script is not defined: %s" % BuildScriptPath - sys.exit(-1) - SBOptions = "-plist-html -o " + SBOutputDir + " " + sys.exit(-1) + SBOptions = "--use-analyzer " + Clang + " " + SBOptions += "-plist-html -o " + SBOutputDir + " " SBOptions += "-enable-checker " + Checkers + " " try: SBCommandFile = open(BuildScriptPath, "r") SBPrefix = "scan-build " + SBOptions + " " for Command in SBCommandFile: + # If using 'make', auto imply a -jX argument + # to speed up analysis. xcodebuild will + # automatically use the maximum number of cores. + if Command.startswith("make "): + Command += "-j" + Jobs SBCommand = SBPrefix + Command if Verbose == 1: print " Executing: %s" % (SBCommand,) @@ -160,17 +239,20 @@ def isValidSingleInputFile(FileName): (Ext == ".m") | (Ext == "")) : return True return False - + # Run analysis on a set of preprocessed files. -def runAnalyzePreprocessed(Dir, SBOutputDir): +def runAnalyzePreprocessed(Dir, SBOutputDir, Mode): if os.path.exists(os.path.join(Dir, BuildScript)): print "Error: The preprocessed files project should not contain %s" % \ BuildScript raise Exception() - CmdPrefix = "clang -cc1 -analyze -analyzer-output=plist -w " + CmdPrefix = Clang + " -cc1 -analyze -analyzer-output=plist -w " CmdPrefix += "-analyzer-checker=" + Checkers +" -fcxx-exceptions -fblocks " + if (Mode == 2) : + CmdPrefix += "-std=c++11 " + PlistPath = os.path.join(Dir, SBOutputDir, "date") FailPath = os.path.join(PlistPath, "failures"); os.makedirs(FailPath); @@ -208,7 +290,7 @@ def runAnalyzePreprocessed(Dir, SBOutputDir): if Failed == False: os.remove(LogFile.name); -def buildProject(Dir, SBOutputDir, IsScanBuild, IsReferenceBuild): +def buildProject(Dir, SBOutputDir, ProjectBuildMode, IsReferenceBuild): TBegin = time.time() BuildLogPath = os.path.join(SBOutputDir, LogFolderName, BuildLogName) @@ -238,10 +320,10 @@ def buildProject(Dir, SBOutputDir, IsScanBuild, IsReferenceBuild): try: runCleanupScript(Dir, PBuildLogFile) - if IsScanBuild: + if (ProjectBuildMode == 1): runScanBuild(Dir, SBOutputDir, PBuildLogFile) else: - runAnalyzePreprocessed(Dir, SBOutputDir) + runAnalyzePreprocessed(Dir, SBOutputDir, ProjectBuildMode) if IsReferenceBuild : runCleanupScript(Dir, PBuildLogFile) @@ -395,7 +477,7 @@ def updateSVN(Mode, ProjectsMap): print "Error: SVN update failed." sys.exit(-1) -def testProject(ID, IsScanBuild, IsReferenceBuild=False, Dir=None): +def testProject(ID, ProjectBuildMode, IsReferenceBuild=False, Dir=None): print " \n\n--- Building project %s" % (ID,) TBegin = time.time() @@ -409,7 +491,7 @@ def testProject(ID, IsScanBuild, IsReferenceBuild=False, Dir=None): RelOutputDir = getSBOutputDirName(IsReferenceBuild) SBOutputDir = os.path.join(Dir, RelOutputDir) - buildProject(Dir, SBOutputDir, IsScanBuild, IsReferenceBuild) + buildProject(Dir, SBOutputDir, ProjectBuildMode, IsReferenceBuild) checkBuild(SBOutputDir) @@ -427,8 +509,9 @@ def testAll(IsReferenceBuild = False, UpdateSVN = False): if (len(I) != 2) : print "Error: Rows in the ProjectMapFile should have 3 entries." raise Exception() - if (not ((I[1] == "1") | (I[1] == "0"))): - print "Error: Second entry in the ProjectMapFile should be 0 or 1." + if (not ((I[1] == "0") | (I[1] == "1") | (I[1] == "2"))): + print "Error: Second entry in the ProjectMapFile should be 0" \ + " (single file), 1 (project), or 2(single file c++11)." raise Exception() # When we are regenerating the reference results, we might need to diff --git a/utils/analyzer/SumTimerInfo.py b/utils/analyzer/SumTimerInfo.py index a6731bb8f243..4ef1ceb4cec5 100644 --- a/utils/analyzer/SumTimerInfo.py +++ b/utils/analyzer/SumTimerInfo.py @@ -28,6 +28,8 @@ if __name__ == '__main__': ReachableBlocks = 0 ReachedMaxSteps = 0 NumSteps = 0 + NumInlinedCallSites = 0 + NumBifurcatedCallSites = 0 MaxCFGSize = 0 Mode = 1 for line in f: @@ -39,25 +41,31 @@ if __name__ == '__main__': Count = Count + 1 if (float(s[6]) > MaxTime) : MaxTime = float(s[6]) - if ((("warning generated." in line) or ("warnings generated." in line)) and Mode == 1) : + if ((("warning generated." in line) or ("warnings generated" in line)) and Mode == 1) : s = line.split() Warnings = Warnings + int(s[0]) - if (("The # of functions analysed (as top level)." in line) and (Mode == 1)) : + if (("The # of functions analysed (as top level)" in line) and (Mode == 1)) : s = line.split() FunctionsAnalyzed = FunctionsAnalyzed + int(s[0]) if (("The % of reachable basic blocks" in line) and (Mode == 1)) : s = line.split() ReachableBlocks = ReachableBlocks + int(s[0]) - if (("The # of times we reached the max number of steps." in line) and (Mode == 1)) : + if (("The # of times we reached the max number of steps" in line) and (Mode == 1)) : s = line.split() ReachedMaxSteps = ReachedMaxSteps + int(s[0]) if (("The maximum number of basic blocks in a function" in line) and (Mode == 1)) : s = line.split() if (MaxCFGSize < int(s[0])) : MaxCFGSize = int(s[0]) - if (("The # of steps executed." in line) and (Mode == 1)) : + if (("The # of steps executed" in line) and (Mode == 1)) : s = line.split() NumSteps = NumSteps + int(s[0]) + if (("The # of times we inlined a call" in line) and (Mode == 1)) : + s = line.split() + NumInlinedCallSites = NumInlinedCallSites + int(s[0]) + if (("The # of times we split the path due to imprecise dynamic dispatch info" in line) and (Mode == 1)) : + s = line.split() + NumBifurcatedCallSites = NumBifurcatedCallSites + int(s[0]) if ((") Total" in line) and (Mode == 1)) : s = line.split() TotalTime = TotalTime + float(s[6]) @@ -69,6 +77,7 @@ if __name__ == '__main__': print "Reachable Blocks %d" % (ReachableBlocks) print "Reached Max Steps %d" % (ReachedMaxSteps) print "Number of Steps %d" % (NumSteps) + print "Number of Inlined calls %d (bifurcated %d)" % (NumInlinedCallSites, NumBifurcatedCallSites) print "MaxTime %f" % (MaxTime) print "TotalTime %f" % (TotalTime) print "Max CFG Size %d" % (MaxCFGSize) diff --git a/utils/analyzer/update_plist_test.pl b/utils/analyzer/update_plist_test.pl new file mode 100755 index 000000000000..abb74a57c3c1 --- /dev/null +++ b/utils/analyzer/update_plist_test.pl @@ -0,0 +1,51 @@ +#!/usr/bin/perl -w +use strict; +require File::Temp; +use File::Temp (); + +die "update_plist_test <test file> <plist file>\n" if ($#ARGV < 1); +my $testFile = shift @ARGV; +die "error: cannot read file $testFile\n" if (! -r $testFile); +my $plistFile = shift @ARGV; +die "error: cannot read file $plistFile\n" if (! -r $plistFile); + +# Create a temp file for the new test. +my $fh = File::Temp->new(); +my $filename = $fh->filename; +$fh->unlink_on_destroy(1); + +# Copy the existing temp file, skipping the FileCheck comments. +open (IN, $testFile) or die "cannot open $testFile\n"; +while (<IN>) { + next if (/^\/\/ CHECK/); + print $fh $_; +} +close(IN); + +# Copy the plist data, and specially format it. +open (IN, $plistFile) or die "cannot open $plistFile\n"; +my $firstArray = 1; +my $first = 1; +while (<IN>) { + # Skip everything not indented. + next if (/^[^\s]/); + # Skip the first array entry, which is for files. + if ($firstArray) { + if (/<\/array>/) { $firstArray = 0; } + next; + } + # Format the CHECK lines. + if ($first) { + print $fh "// CHECK: "; + $first = 0; + } + else { + print $fh "// CHECK-NEXT: "; + } + print $fh $_; +} +close (IN); +close ($fh); + +`cp $filename $testFile`; +print "updated $testFile\n"; |
