diff options
Diffstat (limited to 'utils')
96 files changed, 3963 insertions, 2424 deletions
diff --git a/utils/FileCheck/FileCheck.cpp b/utils/FileCheck/FileCheck.cpp index f73a0fee76c82..6c9b62d5b2442 100644 --- a/utils/FileCheck/FileCheck.cpp +++ b/utils/FileCheck/FileCheck.cpp @@ -45,6 +45,11 @@ InputFilename("input-file", cl::desc("File to check (defaults to stdin)"), static cl::list<std::string> CheckPrefixes("check-prefix", cl::desc("Prefix to use from check file (defaults to 'CHECK')")); +static cl::alias CheckPrefixesAlias( + "check-prefixes", cl::aliasopt(CheckPrefixes), cl::CommaSeparated, + cl::NotHidden, + cl::desc( + "Alias for -check-prefix permitting multiple comma separated values")); static cl::opt<bool> NoCanonicalizeWhiteSpace("strict-whitespace", @@ -62,6 +67,12 @@ static cl::opt<bool> AllowEmptyInput( cl::desc("Allow the input file to be empty. This is useful when making\n" "checks that some error message does not occur, for example.")); +static cl::opt<bool> MatchFullLines( + "match-full-lines", cl::init(false), + cl::desc("Require all positive matches to cover an entire input line.\n" + "Allows leading and trailing whitespace if --strict-whitespace\n" + "is not also passed.")); + typedef cl::list<std::string>::const_iterator prefix_iterator; //===----------------------------------------------------------------------===// @@ -80,7 +91,9 @@ namespace Check { /// MatchEOF - When set, this pattern only matches the end of file. This is /// used for trailing CHECK-NOTs. - CheckEOF + CheckEOF, + /// CheckBadNot - Found -NOT combined with another CHECK suffix. + CheckBadNot }; } @@ -174,6 +187,8 @@ bool Pattern::ParsePattern(StringRef PatternStr, StringRef Prefix, SourceMgr &SM, unsigned LineNumber) { + bool MatchFullLinesHere = MatchFullLines && CheckTy != Check::CheckNot; + this->LineNumber = LineNumber; PatternLoc = SMLoc::getFromPointer(PatternStr.data()); @@ -191,13 +206,19 @@ bool Pattern::ParsePattern(StringRef PatternStr, } // Check to see if this is a fixed string, or if it has regex pieces. - if (PatternStr.size() < 2 || - (PatternStr.find("{{") == StringRef::npos && - PatternStr.find("[[") == StringRef::npos)) { + if (!MatchFullLinesHere && + (PatternStr.size() < 2 || (PatternStr.find("{{") == StringRef::npos && + PatternStr.find("[[") == StringRef::npos))) { FixedStr = PatternStr; return false; } + if (MatchFullLinesHere) { + RegExStr += '^'; + if (!NoCanonicalizeWhiteSpace) + RegExStr += " *"; + } + // Paren value #0 is for the fully matched string. Any new parenthesized // values add from there. unsigned CurParen = 1; @@ -329,6 +350,12 @@ bool Pattern::ParsePattern(StringRef PatternStr, PatternStr = PatternStr.substr(FixedMatchEnd); } + if (MatchFullLinesHere) { + if (!NoCanonicalizeWhiteSpace) + RegExStr += " *"; + RegExStr += '$'; + } + return false; } @@ -598,19 +625,15 @@ struct CheckString { /// CheckTy - Specify what kind of check this is. e.g. CHECK-NEXT: directive, /// as opposed to a CHECK: directive. - Check::CheckType CheckTy; + // Check::CheckType CheckTy; /// DagNotStrings - These are all of the strings that are disallowed from /// occurring between this match string and the previous one (or start of /// file). std::vector<Pattern> DagNotStrings; - - CheckString(const Pattern &P, - StringRef S, - SMLoc L, - Check::CheckType Ty) - : Pat(P), Prefix(S), Loc(L), CheckTy(Ty) {} + CheckString(const Pattern &P, StringRef S, SMLoc L) + : Pat(P), Prefix(S), Loc(L) {} /// Check - Match check string and its "not strings" and/or "dag strings". size_t Check(const SourceMgr &SM, StringRef Buffer, bool IsLabelScanMode, @@ -677,6 +700,7 @@ static bool IsPartOfWord(char c) { static size_t CheckTypeSize(Check::CheckType Ty) { switch (Ty) { case Check::CheckNone: + case Check::CheckBadNot: return 0; case Check::CheckPlain: @@ -730,6 +754,12 @@ static Check::CheckType FindCheckType(StringRef Buffer, StringRef Prefix) { if (Rest.startswith("LABEL:")) return Check::CheckLabel; + // You can't combine -NOT with another suffix. + if (Rest.startswith("DAG-NOT:") || Rest.startswith("NOT-DAG:") || + Rest.startswith("NEXT-NOT:") || Rest.startswith("NOT-NEXT:") || + Rest.startswith("SAME-NOT:") || Rest.startswith("NOT-SAME:")) + return Check::CheckBadNot; + return Check::CheckNone; } @@ -898,6 +928,14 @@ static bool ReadCheckFile(SourceMgr &SM, // PrefixLoc is to the start of the prefix. Skip to the end. Buffer = Buffer.drop_front(UsedPrefix.size() + CheckTypeSize(CheckTy)); + // Complain about useful-looking but unsupported suffixes. + if (CheckTy == Check::CheckBadNot) { + SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), + SourceMgr::DK_Error, + "unsupported -NOT combo on prefix '" + UsedPrefix + "'"); + return true; + } + // Okay, we found the prefix, yay. Remember the rest of the line, but ignore // leading and trailing whitespace. Buffer = Buffer.substr(Buffer.find_first_not_of(" \t")); @@ -942,7 +980,7 @@ static bool ReadCheckFile(SourceMgr &SM, } // Okay, add the string we captured to the output vector and move on. - CheckStrings.emplace_back(P, UsedPrefix, PatternLoc, CheckTy); + CheckStrings.emplace_back(P, UsedPrefix, PatternLoc); std::swap(DagNotMatches, CheckStrings.back().DagNotStrings); DagNotMatches = ImplicitNegativeChecks; } @@ -951,8 +989,7 @@ static bool ReadCheckFile(SourceMgr &SM, // prefix as a filler for the error message. if (!DagNotMatches.empty()) { CheckStrings.emplace_back(Pattern(Check::CheckEOF), *CheckPrefixes.begin(), - SMLoc::getFromPointer(Buffer.data()), - Check::CheckEOF); + SMLoc::getFromPointer(Buffer.data())); std::swap(DagNotMatches, CheckStrings.back().DagNotStrings); } @@ -965,7 +1002,7 @@ static bool ReadCheckFile(SourceMgr &SM, errs() << "\'" << *I << ":'"; ++I; } - for (; I != E; ++I) + for (; I != E; ++I) errs() << ", \'" << *I << ":'"; errs() << '\n'; @@ -1073,7 +1110,7 @@ size_t CheckString::Check(const SourceMgr &SM, StringRef Buffer, } bool CheckString::CheckNext(const SourceMgr &SM, StringRef Buffer) const { - if (CheckTy != Check::CheckNext) + if (Pat.getCheckTy() != Check::CheckNext) return false; // Count the number of newlines between the previous match and this one. @@ -1112,7 +1149,7 @@ bool CheckString::CheckNext(const SourceMgr &SM, StringRef Buffer) const { } bool CheckString::CheckSame(const SourceMgr &SM, StringRef Buffer) const { - if (CheckTy != Check::CheckSame) + if (Pat.getCheckTy() != Check::CheckSame) return false; // Count the number of newlines between the previous match and this one. @@ -1266,8 +1303,15 @@ static void AddCheckPrefixIfNeeded() { CheckPrefixes.push_back("CHECK"); } +static void DumpCommandLine(int argc, char **argv) { + errs() << "FileCheck command line: "; + for (int I = 0; I < argc; I++) + errs() << " " << argv[I]; + errs() << "\n"; +} + int main(int argc, char **argv) { - sys::PrintStackTraceOnErrorSignal(); + sys::PrintStackTraceOnErrorSignal(argv[0]); PrettyStackTraceProgram X(argc, argv); cl::ParseCommandLineOptions(argc, argv); @@ -1299,6 +1343,7 @@ int main(int argc, char **argv) { if (File->getBufferSize() == 0 && !AllowEmptyInput) { errs() << "FileCheck error: '" << InputFilename << "' is empty.\n"; + DumpCommandLine(argc, argv); return 2; } @@ -1326,7 +1371,7 @@ int main(int argc, char **argv) { CheckRegion = Buffer; } else { const CheckString &CheckLabelStr = CheckStrings[j]; - if (CheckLabelStr.CheckTy != Check::CheckLabel) { + if (CheckLabelStr.Pat.getCheckTy() != Check::CheckLabel) { ++j; continue; } diff --git a/utils/FileCheck/Makefile b/utils/FileCheck/Makefile deleted file mode 100644 index b8762365984d5..0000000000000 --- a/utils/FileCheck/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -##===- utils/FileCheck/Makefile ----------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../.. -TOOLNAME = FileCheck -USEDLIBS = LLVMSupport.a - -# This tool has no plugins, optimize startup time. -TOOL_NO_EXPORTS = 1 - -# FIXME: Don't install this utility -#NO_INSTALL = 1 - -include $(LEVEL)/Makefile.common - diff --git a/utils/GenLibDeps.pl b/utils/GenLibDeps.pl index 7748cabdab5b2..9b65e900c53e8 100755 --- a/utils/GenLibDeps.pl +++ b/utils/GenLibDeps.pl @@ -96,7 +96,6 @@ if ($PEROBJ) { $libpath =~ s/^AsmPrinter/CodeGen\/AsmPrinter/; $libpath =~ s/^BitReader/Bitcode\/Reader/; $libpath =~ s/^BitWriter/Bitcode\/Writer/; - $libpath =~ s/^CppBackend/Target\/CppBackend/; $libpath =~ s/^MSIL/Target\/MSIL/; $libpath =~ s/^Core/IR/; $libpath =~ s/^Instrumentation/Transforms\/Instrumentation/; @@ -137,7 +136,6 @@ if ($PEROBJ) { $libpath =~ s/^AsmPrinter/CodeGen\/AsmPrinter/; $libpath =~ s/^BitReader/Bitcode\/Reader/; $libpath =~ s/^BitWriter/Bitcode\/Writer/; - $libpath =~ s/^CppBackend/Target\/CppBackend/; $libpath =~ s/^MSIL/Target\/MSIL/; $libpath =~ s/^Core/VMCore/; $libpath =~ s/^Instrumentation/Transforms\/Instrumentation/; diff --git a/utils/KillTheDoctor/KillTheDoctor.cpp b/utils/KillTheDoctor/KillTheDoctor.cpp index 6c2242aafdfe7..c9e96617f37fc 100644 --- a/utils/KillTheDoctor/KillTheDoctor.cpp +++ b/utils/KillTheDoctor/KillTheDoctor.cpp @@ -296,7 +296,7 @@ static StringRef ExceptionCodeToString(DWORD ExceptionCode) { int main(int argc, char **argv) { // Print a stack trace if we signal out. - sys::PrintStackTraceOnErrorSignal(); + sys::PrintStackTraceOnErrorSignal(argv[0]); PrettyStackTraceProgram X(argc, argv); llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. @@ -337,7 +337,7 @@ int main(int argc, char **argv) { errs() << ToolName << ": Program Image Path: " << ProgramToRun << '\n' << ToolName << ": Command Line: " << CommandLine << '\n'; - STARTUPINFO StartupInfo; + STARTUPINFOA StartupInfo; PROCESS_INFORMATION ProcessInfo; std::memset(&StartupInfo, 0, sizeof(StartupInfo)); StartupInfo.cb = sizeof(StartupInfo); diff --git a/utils/LLVMVisualizers/CMakeLists.txt b/utils/LLVMVisualizers/CMakeLists.txt new file mode 100644 index 0000000000000..9fdc8906e55ae --- /dev/null +++ b/utils/LLVMVisualizers/CMakeLists.txt @@ -0,0 +1,7 @@ +# Do this by hand instead of using add_llvm_utilities(), which +# tries to create a corresponding executable, which we don't want. +if (LLVM_ADD_NATIVE_VISUALIZERS_TO_SOLUTION) + set(LLVM_VISUALIZERS llvm.natvis) + add_custom_target(LLVMVisualizers SOURCES ${LLVM_VISUALIZERS}) + set_target_properties(LLVMVisualizers PROPERTIES FOLDER "Utils") +endif() diff --git a/utils/LLVMVisualizers/llvm.natvis b/utils/LLVMVisualizers/llvm.natvis new file mode 100644 index 0000000000000..6d8475a1c72e1 --- /dev/null +++ b/utils/LLVMVisualizers/llvm.natvis @@ -0,0 +1,246 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!--
+Visual Studio Native Debugging Visualizers for LLVM
+
+For Visual Studio 2013 only, put this file into
+"%USERPROFILE%\Documents\Visual Studio 2013\Visualizers" or create a symbolic link so it updates automatically.
+
+For later versions of Visual Studio, no setup is required.
+-->
+<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
+ <!-- VS2013 -->
+ <Type Name="llvm::SmallVectorImpl<*>" Priority="MediumLow">
+ <DisplayString Condition="(($T1*)EndX - ($T1*)BeginX) == 0">empty</DisplayString>
+ <DisplayString Condition="(($T1*)EndX - ($T1*)BeginX) != 0">{{ size={($T1*)EndX - ($T1*)BeginX} }}</DisplayString>
+ <Expand>
+ <Item Name="[size]">($T1*)EndX - ($T1*)BeginX</Item>
+ <Item Name="[capacity]">($T1*)CapacityX - ($T1*)BeginX</Item>
+ <ArrayItems>
+ <Size>($T1*)EndX - ($T1*)BeginX</Size>
+ <ValuePointer>($T1*)BeginX</ValuePointer>
+ </ArrayItems>
+ </Expand>
+ </Type>
+ <!-- VS2015 and up -->
+ <Type Name="llvm::SmallVectorImpl<*>">
+ <DisplayString IncludeView ="elt0" Condition="(($T1*)EndX - ($T1*)BeginX) == 0"></DisplayString>
+ <DisplayString IncludeView ="elt0">{(($T1*)BeginX)[0]}{*this,view(elt1)}</DisplayString>
+ <DisplayString IncludeView ="elt1" Condition="(($T1*)EndX - ($T1*)BeginX) == 1"></DisplayString>
+ <DisplayString IncludeView ="elt1">, {(($T1*)BeginX)[1]}{*this,view(elt2)}</DisplayString>
+ <DisplayString IncludeView ="elt2" Condition="(($T1*)EndX - ($T1*)BeginX) == 2"></DisplayString>
+ <DisplayString IncludeView ="elt2">, {(($T1*)BeginX)[2]}{*this,view(elt3)}</DisplayString>
+ <DisplayString IncludeView ="elt3" Condition="(($T1*)EndX - ($T1*)BeginX) == 3"></DisplayString>
+ <DisplayString IncludeView ="elt3">, {(($T1*)BeginX)[2]}{*this,view(elt4)}</DisplayString>
+ <DisplayString IncludeView ="elt4" Condition="(($T1*)EndX - ($T1*)BeginX) == 4"></DisplayString>
+ <DisplayString IncludeView ="elt4">, /* {(($T1*)EndX - ($T1*)BeginX) - 4} more*/ </DisplayString>
+ <DisplayString Condition="(($T1*)EndX - ($T1*)BeginX) == 0">empty</DisplayString>
+ <DisplayString Condition="(($T1*)EndX - ($T1*)BeginX) != 0">{{{*this,view(elt0)}}}</DisplayString>
+ <Expand>
+ <Item Name="[size]">($T1*)EndX - ($T1*)BeginX</Item>
+ <Item Name="[capacity]">($T1*)CapacityX - ($T1*)BeginX</Item>
+ <ArrayItems>
+ <Size>($T1*)EndX - ($T1*)BeginX</Size>
+ <ValuePointer>($T1*)BeginX</ValuePointer>
+ </ArrayItems>
+ </Expand>
+ </Type>
+ <Type Name="llvm::ArrayRef<*>">
+ <DisplayString Condition="Length == 0">empty</DisplayString>
+ <DisplayString Condition="Length != 0">{{ size={Length} }}</DisplayString>
+ <Expand>
+ <Item Name="[size]">Length</Item>
+ <ArrayItems>
+ <Size>Length</Size>
+ <ValuePointer>Data</ValuePointer>
+ </ArrayItems>
+ </Expand>
+ </Type>
+ <Type Name="llvm::SmallString<*>">
+ <DisplayString>{BeginX,s}</DisplayString>
+ <StringView>BeginX,s</StringView>
+ <Expand>
+ <Item Name="[size]">(char*)EndX - (char*)BeginX</Item>
+ <Item Name="[capacity]">(char*)CapacityX - (char*)BeginX</Item>
+ <ArrayItems>
+ <Size>(char*)EndX - (char*)BeginX</Size>
+ <ValuePointer>(char*)BeginX</ValuePointer>
+ </ArrayItems>
+ </Expand>
+ </Type>
+
+ <Type Name="llvm::StringRef">
+ <DisplayString>{Data,[Length]s}</DisplayString>
+ <StringView>Data,[Length]s</StringView>
+ <Expand>
+ <Item Name="[size]">Length</Item>
+ <ArrayItems>
+ <Size>Length</Size>
+ <ValuePointer>Data</ValuePointer>
+ </ArrayItems>
+ </Expand>
+ </Type>
+
+ <Type Name="llvm::PointerIntPair<*,*,*,*>">
+ <DisplayString>{IntMask}: {($T1)(Value & PointerBitMask)} [{($T3)((Value >> IntShift) & IntMask)}]</DisplayString>
+ <Expand>
+ <Item Name="[ptr]">($T1)(Value & PointerBitMask)</Item>
+ <Item Name="[int]">($T3)((Value >> IntShift) & IntMask)</Item>
+ </Expand>
+ </Type>
+
+ <Type Name="llvm::PointerUnion<*,*>">
+ <DisplayString Condition="((Val.Value >> Val.IntShift) & Val.IntMask) == 0">{"$T1", s8b}: {($T1)(Val.Value & Val.PointerBitMask)}</DisplayString>
+ <DisplayString Condition="((Val.Value >> Val.IntShift) & Val.IntMask) != 0">{"$T2", s8b}: {($T2)(Val.Value & Val.PointerBitMask)}</DisplayString>
+ <Expand>
+ <ExpandedItem Condition="((Val.Value >> Val.IntShift) & Val.IntMask) == 0">($T1)(Val.Value & Val.PointerBitMask)</ExpandedItem>
+ <ExpandedItem Condition="((Val.Value >> Val.IntShift) & Val.IntMask) != 0">($T2)(Val.Value & Val.PointerBitMask)</ExpandedItem>
+ </Expand>
+ </Type>
+
+ <Type Name="llvm::PointerUnion3<*,*,*>">
+ <DisplayString Condition="(Val.Val.Value & 2) != 2 && (Val.Val.Value & 1) != 1">{"$T1", s8b}: {($T1)((Val.Val.Value >> 2) << 2)}</DisplayString>
+ <DisplayString Condition="(Val.Val.Value & 2) == 2">{"$T2", s8b}: {($T2)((Val.Val.Value >> 2) << 2)}</DisplayString>
+ <DisplayString Condition="(Val.Val.Value & 1) == 1">{"$T3", s8b}: {($T3)((Val.Val.Value >> 2) << 2)}</DisplayString>
+ <Expand>
+ <ExpandedItem Condition="(Val.Val.Value & 2) != 2 && (Val.Val.Value & 1) != 1">($T1)((Val.Val.Value >> 2) << 2)</ExpandedItem>
+ <ExpandedItem Condition="(Val.Val.Value & 2) == 2">($T2)((Val.Val.Value >> 2) << 2)</ExpandedItem>
+ <ExpandedItem Condition="(Val.Val.Value & 1) == 1">($T3)((Val.Val.Value >> 2) << 2)</ExpandedItem>
+ </Expand>
+ </Type>
+
+ <Type Name="llvm::PointerUnion4<*,*,*,*>">
+ <DisplayString Condition="(Val.Val.Value & 3) != 3 && (Val.Val.Value & 2) != 2 && (Val.Val.Value & 1) != 1">{"$T1", s8b}: {($T1)((Val.Val.Value >> 2) << 2)}</DisplayString>
+ <DisplayString Condition="(Val.Val.Value & 3) != 3 && (Val.Val.Value & 2) == 2">{"$T2", s8b}: {($T2)((Val.Val.Value >> 2) << 2)}</DisplayString>
+ <DisplayString Condition="(Val.Val.Value & 3) != 3 && (Val.Val.Value & 1) == 1">{"$T3", s8b}: {($T3)((Val.Val.Value >> 2) << 2)}</DisplayString>
+ <DisplayString Condition="(Val.Val.Value & 3) == 3">{"$T4", s8b}: {($T4)((Val.Val.Value >> 2) << 2)}</DisplayString>
+ <Expand>
+ <ExpandedItem Condition="(Val.Val.Value & 3) != 3 && (Val.Val.Value & 2) != 2 && (Val.Val.Value & 1) != 1">($T1)((Val.Val.Value >> 2) << 2)</ExpandedItem>
+ <ExpandedItem Condition="(Val.Val.Value & 3) != 3 && (Val.Val.Value & 2) == 2">($T2)((Val.Val.Value >> 2) << 2)</ExpandedItem>
+ <ExpandedItem Condition="(Val.Val.Value & 3) != 3 && (Val.Val.Value & 1) == 1">($T3)((Val.Val.Value >> 2) << 2)</ExpandedItem>
+ <ExpandedItem Condition="(Val.Val.Value & 3) == 3">($T4)((Val.Val.Value >> 2) << 2)</ExpandedItem>
+ </Expand>
+ </Type>
+
+ <Type Name="llvm::iplist<*,*>">
+ <DisplayString Condition="Head == 0">{{ empty }}</DisplayString>
+ <DisplayString Condition="Head != 0">{{ head={Head} }}</DisplayString>
+ <Expand>
+ <LinkedListItems>
+ <HeadPointer>Head</HeadPointer>
+ <NextPointer>Next</NextPointer>
+ <ValueNode>this</ValueNode>
+ </LinkedListItems>
+ </Expand>
+ </Type>
+
+ <Type Name="llvm::IntrusiveRefCntPtr<*>">
+ <DisplayString Condition="Obj == 0">empty</DisplayString>
+ <DisplayString Condition="(Obj != 0) && (Obj->ref_cnt == 1)">RefPtr [1 ref] {*Obj}</DisplayString>
+ <DisplayString Condition="(Obj != 0) && (Obj->ref_cnt != 1)">RefPtr [{Obj->ref_cnt} refs] {*Obj}</DisplayString>
+ <Expand>
+ <Item Condition="Obj != 0" Name="[refs]">Obj->ref_cnt</Item>
+ <ExpandedItem Condition="Obj != 0">Obj</ExpandedItem>
+ </Expand>
+ </Type>
+
+ <Type Name="llvm::SmallPtrSet<*,*>">
+ <DisplayString Condition="CurArray == SmallArray">{{ [Small Mode] size={NumElements}, capacity={CurArraySize} }}</DisplayString>
+ <DisplayString Condition="CurArray != SmallArray">{{ [Big Mode] size={NumElements}, capacity={CurArraySize} }}</DisplayString>
+ <Expand>
+ <Item Name="[size]">NumElements</Item>
+ <Item Name="[capacity]">CurArraySize</Item>
+ <ArrayItems>
+ <Size>CurArraySize</Size>
+ <ValuePointer>($T1*)CurArray</ValuePointer>
+ </ArrayItems>
+ </Expand>
+ </Type>
+
+ <Type Name="llvm::DenseMap<*,*,*>">
+ <DisplayString Condition="NumEntries == 0">empty</DisplayString>
+ <DisplayString Condition="NumEntries != 0">{{ size={NumEntries}, buckets={NumBuckets} }}</DisplayString>
+ <Expand>
+ <Item Name="[size]">NumEntries</Item>
+ <Item Name="[buckets]">NumBuckets</Item>
+ <ArrayItems>
+ <Size>NumBuckets</Size>
+ <ValuePointer>Buckets</ValuePointer>
+ </ArrayItems>
+ </Expand>
+ </Type>
+
+ <Type Name="llvm::StringMap<*,*>">
+ <DisplayString>{{ size={NumItems}, buckets={NumBuckets} }}</DisplayString>
+ <Expand>
+ <Item Name="[size]">NumItems</Item>
+ <Item Name="[buckets]">NumBuckets</Item>
+ <ArrayItems>
+ <Size>NumBuckets</Size>
+ <ValuePointer>(MapEntryTy**)TheTable</ValuePointer>
+ </ArrayItems>
+ </Expand>
+ </Type>
+
+ <Type Name="llvm::StringMapEntry<*>">
+ <DisplayString Condition="StrLen == 0">empty</DisplayString>
+ <DisplayString Condition="StrLen != 0">({this+1,s}, {second})</DisplayString>
+ <Expand>
+ <Item Name="[key]">this+1,s</Item>
+ <Item Name="[value]" Condition="StrLen != 0">second</Item>
+ </Expand>
+ </Type>
+
+ <Type Name="llvm::Triple">
+ <DisplayString>{Data}</DisplayString>
+ </Type>
+
+ <Type Name="llvm::Optional<*>">
+ <DisplayString Condition="!hasVal">empty</DisplayString>
+ <DisplayString Condition="hasVal">{*(($T1 *)(unsigned char *)storage.buffer)}</DisplayString>
+ <Expand>
+ <Item Name="[underlying]" Condition="hasVal">*(($T1 *)(unsigned char *)storage.buffer)</Item>
+ </Expand>
+ </Type>
+
+
+ <!-- Since we're in MSVC, we can assume that the system is little endian. Therefore
+ the little and native cases just require a cast. Handle this easy case first. Use
+ a wildcard for the second template argument (the endianness), but we will use a
+ specific value of 0 later on for the big endian to give it priority for being a
+ better match. -->
+ <Type Name="llvm::support::detail::packed_endian_specific_integral<*,*,1>">
+ <DisplayString>{{little endian value = {*(($T1*)(unsigned char *)Value.buffer)} }}</DisplayString>
+ <Expand>
+ <Item Name="[Raw Bytes]" Condition="sizeof($T1)==1">(unsigned char *)Value.buffer,1</Item>
+ <Item Name="[Raw Bytes]" Condition="sizeof($T1)==2">(unsigned char *)Value.buffer,2</Item>
+ <Item Name="[Raw Bytes]" Condition="sizeof($T1)==4">(unsigned char *)Value.buffer,4</Item>
+ <Item Name="[Raw Bytes]" Condition="sizeof($T1)==8">(unsigned char *)Value.buffer,8</Item>
+ </Expand>
+ </Type>
+
+ <!-- Now handle the hard case of big endian. We need to do the swizzling here, but
+ we need to specialize it based on the size of the value type. -->
+ <Type Name="llvm::support::detail::packed_endian_specific_integral<*,0,1>">
+ <DisplayString Condition="sizeof($T1)==1">{{ big endian value = {*(unsigned char *)Value.buffer} }}</DisplayString>
+ <DisplayString Condition="sizeof($T1)==2">{{ big endian value = {(($T1)(*(unsigned char *)Value.buffer) << 8)
+ | ($T1)(*((unsigned char *)Value.buffer+1))} }}</DisplayString>
+ <DisplayString Condition="sizeof($T1)==4">{{ big endian value = {(($T1)(*(unsigned char *)Value.buffer) << 24)
+ | (($T1)(*((unsigned char *)Value.buffer+1)) << 16)
+ | (($T1)(*((unsigned char *)Value.buffer+2)) << 8)
+ | ($T1)(*((unsigned char *)Value.buffer+3))} }}</DisplayString>
+ <DisplayString Condition="sizeof($T1)==8">{{ big endian value = {(($T1)(*(unsigned char *)Value.buffer) << 56)
+ | (($T1)(*((unsigned char *)Value.buffer+1)) << 48)
+ | (($T1)(*((unsigned char *)Value.buffer+2)) << 40)
+ | (($T1)(*((unsigned char *)Value.buffer+3)) << 32)
+ | (($T1)(*((unsigned char *)Value.buffer+4)) << 24)
+ | (($T1)(*((unsigned char *)Value.buffer+5)) << 16)
+ | (($T1)(*((unsigned char *)Value.buffer+6)) << 8)
+ | ($T1)(*((unsigned char *)Value.buffer+7))} }}</DisplayString>
+ <Expand>
+ <Item Name="[Raw Bytes]" Condition="sizeof($T1)==1">(unsigned char *)Value.buffer,1</Item>
+ <Item Name="[Raw Bytes]" Condition="sizeof($T1)==2">(unsigned char *)Value.buffer,2</Item>
+ <Item Name="[Raw Bytes]" Condition="sizeof($T1)==4">(unsigned char *)Value.buffer,4</Item>
+ <Item Name="[Raw Bytes]" Condition="sizeof($T1)==8">(unsigned char *)Value.buffer,8</Item>
+ </Expand>
+ </Type>
+</AutoVisualizer>
diff --git a/utils/Makefile b/utils/Makefile deleted file mode 100644 index a59318de4834e..0000000000000 --- a/utils/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -##===- utils/Makefile --------------------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = .. -PARALLEL_DIRS := FileCheck TableGen PerfectShuffle count fpcmp llvm-lit not \ - unittest yaml-bench - -EXTRA_DIST := check-each-file codegen-diff countloc.sh \ - DSAclean.py DSAextract.py emacs findsym.pl GenLibDeps.pl \ - getsrcs.sh llvmdo llvmgrep llvm-native-gcc \ - llvm-native-gxx makellvm profile.pl vim - -include $(LEVEL)/Makefile.common diff --git a/utils/Misc/mergefunctions.clang.svn.patch b/utils/Misc/mergefunctions.clang.svn.patch deleted file mode 100644 index 6e2f0f5227985..0000000000000 --- a/utils/Misc/mergefunctions.clang.svn.patch +++ /dev/null @@ -1,14 +0,0 @@ -Index: lib/CodeGen/BackendUtil.cpp -=================================================================== ---- lib/CodeGen/BackendUtil.cpp (revision 191330) -+++ lib/CodeGen/BackendUtil.cpp (working copy) -@@ -336,6 +336,9 @@ - MPM->add(createStripSymbolsPass(true)); - } - -+ // Force MergeFunctions pass. -+ MPM->add(createMergeFunctionsPass()); -+ - PMBuilder.populateModulePassManager(*MPM); - } - diff --git a/utils/PerfectShuffle/Makefile b/utils/PerfectShuffle/Makefile deleted file mode 100644 index 28709fefd319b..0000000000000 --- a/utils/PerfectShuffle/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -##===- utils/PerfectShuffle/Makefile -----------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../.. -TOOLNAME = llvm-PerfectShuffle -NO_INSTALL = 1 - -# This tool has no plugins, optimize startup time. -TOOL_NO_EXPORTS = 1 - -include $(LEVEL)/Makefile.common - diff --git a/utils/TableGen/AsmMatcherEmitter.cpp b/utils/TableGen/AsmMatcherEmitter.cpp index 6e9a9484dc886..76228e0b6581e 100644 --- a/utils/TableGen/AsmMatcherEmitter.cpp +++ b/utils/TableGen/AsmMatcherEmitter.cpp @@ -112,10 +112,10 @@ #include "llvm/TableGen/TableGenBackend.h" #include <cassert> #include <cctype> +#include <forward_list> #include <map> #include <set> -#include <sstream> -#include <forward_list> + using namespace llvm; #define DEBUG_TYPE "asm-matcher-emitter" @@ -199,6 +199,14 @@ struct ClassInfo { /// For custom match classes: the diagnostic kind for when the predicate fails. std::string DiagnosticType; + + /// Is this operand optional and not always required. + bool IsOptional; + + /// DefaultMethod - The name of the method that returns the default operand + /// for optional operand + std::string DefaultMethod; + public: /// isRegisterClass() - Check if this is a register class. bool isRegisterClass() const { @@ -263,34 +271,79 @@ public: return false; } - /// operator< - Compare two classes. - // FIXME: This ordering seems to be broken. For example: - // u64 < i64, i64 < s8, s8 < u64, forming a cycle - // u64 is a subset of i64 - // i64 and s8 are not subsets of each other, so are ordered by name - // s8 and u64 are not subsets of each other, so are ordered by name + int getTreeDepth() const { + int Depth = 0; + const ClassInfo *Root = this; + while (!Root->SuperClasses.empty()) { + Depth++; + Root = Root->SuperClasses.front(); + } + return Depth; + } + + const ClassInfo *findRoot() const { + const ClassInfo *Root = this; + while (!Root->SuperClasses.empty()) + Root = Root->SuperClasses.front(); + return Root; + } + + /// Compare two classes. This does not produce a total ordering, but does + /// guarantee that subclasses are sorted before their parents, and that the + /// ordering is transitive. bool operator<(const ClassInfo &RHS) const { if (this == &RHS) return false; - // Unrelated classes can be ordered by kind. - if (!isRelatedTo(RHS)) - return Kind < RHS.Kind; - - switch (Kind) { - case Invalid: - llvm_unreachable("Invalid kind!"); - - default: - // This class precedes the RHS if it is a proper subset of the RHS. - if (isSubsetOf(RHS)) + // First, enforce the ordering between the three different types of class. + // Tokens sort before registers, which sort before user classes. + if (Kind == Token) { + if (RHS.Kind != Token) return true; - if (RHS.isSubsetOf(*this)) + assert(RHS.Kind == Token); + } else if (isRegisterClass()) { + if (RHS.Kind == Token) return false; + else if (RHS.isUserClass()) + return true; + assert(RHS.isRegisterClass()); + } else if (isUserClass()) { + if (!RHS.isUserClass()) + return false; + assert(RHS.isUserClass()); + } else { + llvm_unreachable("Unknown ClassInfoKind"); + } - // Otherwise, order by name to ensure we have a total ordering. - return ValueName < RHS.ValueName; + if (Kind == Token || isUserClass()) { + // Related tokens and user classes get sorted by depth in the inheritence + // tree (so that subclasses are before their parents). + if (isRelatedTo(RHS)) { + if (getTreeDepth() > RHS.getTreeDepth()) + return true; + if (getTreeDepth() < RHS.getTreeDepth()) + return false; + } else { + // Unrelated tokens and user classes are ordered by the name of their + // root nodes, so that there is a consistent ordering between + // unconnected trees. + return findRoot()->ValueName < RHS.findRoot()->ValueName; + } + } else if (isRegisterClass()) { + // For register sets, sort by number of registers. This guarantees that + // a set will always sort before all of it's strict supersets. + if (Registers.size() != RHS.Registers.size()) + return Registers.size() < RHS.Registers.size(); + } else { + llvm_unreachable("Unknown ClassInfoKind"); } + + // FIXME: We should be able to just return false here, as we only need a + // partial order (we use stable sorts, so this is deterministic) and the + // name of a class shouldn't be significant. However, some of the backends + // accidentally rely on this behaviour, so it will have to stay like this + // until they are fixed. + return ValueName < RHS.ValueName; } }; @@ -526,8 +579,8 @@ struct MatchableInfo { /// operator< - Compare two matchables. bool operator<(const MatchableInfo &RHS) const { // The primary comparator is the instruction mnemonic. - if (Mnemonic != RHS.Mnemonic) - return Mnemonic < RHS.Mnemonic; + if (int Cmp = Mnemonic.compare(RHS.Mnemonic)) + return Cmp == -1; if (AsmOperands.size() != RHS.AsmOperands.size()) return AsmOperands.size() < RHS.AsmOperands.size(); @@ -637,7 +690,6 @@ struct OperandMatchEntry { } }; - class AsmMatcherInfo { public: /// Tracked Records @@ -720,9 +772,15 @@ public: RecordKeeper &getRecords() const { return Records; } + + bool hasOptionalOperands() const { + return std::find_if(Classes.begin(), Classes.end(), + [](const ClassInfo& Class){ return Class.IsOptional; }) + != Classes.end(); + } }; -} // End anonymous namespace +} // end anonymous namespace void MatchableInfo::dump() const { errs() << TheDef->getName() << " -- " << "flattened:\"" << AsmString <<"\"\n"; @@ -833,7 +891,6 @@ extractSingletonRegisterForAsmOperand(MatchableInfo::AsmOperand &Op, // If there is no register prefix (i.e. "%" in "%eax"), then this may // be some random non-register token, just ignore it. - return; } void MatchableInfo::initialize(const AsmMatcherInfo &Info, @@ -1071,6 +1128,8 @@ ClassInfo *AsmMatcherInfo::getTokenClass(StringRef Token) { Entry->RenderMethod = "<invalid>"; Entry->ParserMethod = ""; Entry->DiagnosticType = ""; + Entry->IsOptional = false; + Entry->DefaultMethod = "<invalid>"; } return Entry; @@ -1111,7 +1170,6 @@ AsmMatcherInfo::getOperandClass(Record *Rec, int SubOpIdx) { PrintFatalError(Rec->getLoc(), "register class has no class info!"); } - if (Rec->isSubClassOf("RegisterClass")) { if (ClassInfo *CI = RegisterClassClasses[Rec]) return CI; @@ -1206,6 +1264,8 @@ buildRegisterClasses(SmallPtrSetImpl<Record*> &SingletonRegisters) { CI->Registers = RS; // FIXME: diagnostic type. CI->DiagnosticType = ""; + CI->IsOptional = false; + CI->DefaultMethod = ""; // unused RegisterSetClasses.insert(std::make_pair(RS, CI)); ++Index; } @@ -1320,6 +1380,19 @@ void AsmMatcherInfo::buildOperandClasses() { if (StringInit *SI = dyn_cast<StringInit>(DiagnosticType)) CI->DiagnosticType = SI->getValue(); + Init *IsOptional = Rec->getValueInit("IsOptional"); + if (BitInit *BI = dyn_cast<BitInit>(IsOptional)) + CI->IsOptional = BI->getValue(); + + // Get or construct the default method name. + Init *DMName = Rec->getValueInit("DefaultMethod"); + if (StringInit *SI = dyn_cast<StringInit>(DMName)) { + CI->DefaultMethod = SI->getValue(); + } else { + assert(isa<UnsetInit>(DMName) && "Unexpected DefaultMethod field!"); + CI->DefaultMethod = "default" + CI->ClassName + "Operands"; + } + ++Index; } } @@ -1400,7 +1473,7 @@ void AsmMatcherInfo::buildInfo() { AsmVariant->getValueAsString("BreakCharacters"); Variant.AsmVariantNo = AsmVariant->getValueAsInt("Variant"); - for (const CodeGenInstruction *CGI : Target.instructions()) { + for (const CodeGenInstruction *CGI : Target.getInstructionsByEnumValue()) { // If the tblgen -match-prefix option is specified (for tblgen hackers), // filter the set of instructions we consider. @@ -1539,6 +1612,16 @@ void AsmMatcherInfo::buildInfo() { // Reorder classes so that classes precede super classes. Classes.sort(); + +#ifndef NDEBUG + // Verify that the table is now sorted + for (auto I = Classes.begin(), E = Classes.end(); I != E; ++I) { + for (auto J = I; J != E; ++J) { + assert(!(*J < *I)); + assert(I == J || !J->isSubsetOf(*I)); + } + } +#endif // NDEBUG } /// buildInstructionOperandReference - The specified operand is a reference to a @@ -1744,10 +1827,10 @@ static unsigned getConverterOperandID(const std::string &Name, return ID; } - static void emitConvertFuncs(CodeGenTarget &Target, StringRef ClassName, std::vector<std::unique_ptr<MatchableInfo>> &Infos, - bool HasMnemonicFirst, raw_ostream &OS) { + bool HasMnemonicFirst, bool HasOptionalOperands, + raw_ostream &OS) { SmallSetVector<std::string, 16> OperandConversionKinds; SmallSetVector<std::string, 16> InstructionConversionKinds; std::vector<std::vector<uint8_t> > ConversionTable; @@ -1762,24 +1845,40 @@ static void emitConvertFuncs(CodeGenTarget &Target, StringRef ClassName, std::string ConvertFnBody; raw_string_ostream CvtOS(ConvertFnBody); // Start the unified conversion function. - CvtOS << "void " << Target.getName() << ClassName << "::\n" - << "convertToMCInst(unsigned Kind, MCInst &Inst, " - << "unsigned Opcode,\n" - << " const OperandVector" - << " &Operands) {\n" - << " assert(Kind < CVT_NUM_SIGNATURES && \"Invalid signature!\");\n" - << " const uint8_t *Converter = ConversionTable[Kind];\n" - << " Inst.setOpcode(Opcode);\n" - << " for (const uint8_t *p = Converter; *p; p+= 2) {\n" - << " switch (*p) {\n" - << " default: llvm_unreachable(\"invalid conversion entry!\");\n" - << " case CVT_Reg:\n" - << " static_cast<" << TargetOperandClass - << "&>(*Operands[*(p + 1)]).addRegOperands(Inst, 1);\n" - << " break;\n" - << " case CVT_Tied:\n" - << " Inst.addOperand(Inst.getOperand(*(p + 1)));\n" - << " break;\n"; + if (HasOptionalOperands) { + CvtOS << "void " << Target.getName() << ClassName << "::\n" + << "convertToMCInst(unsigned Kind, MCInst &Inst, " + << "unsigned Opcode,\n" + << " const OperandVector &Operands,\n" + << " const SmallBitVector &OptionalOperandsMask) {\n"; + } else { + CvtOS << "void " << Target.getName() << ClassName << "::\n" + << "convertToMCInst(unsigned Kind, MCInst &Inst, " + << "unsigned Opcode,\n" + << " const OperandVector &Operands) {\n"; + } + CvtOS << " assert(Kind < CVT_NUM_SIGNATURES && \"Invalid signature!\");\n"; + CvtOS << " const uint8_t *Converter = ConversionTable[Kind];\n"; + if (HasOptionalOperands) { + CvtOS << " unsigned NumDefaults = 0;\n"; + } + CvtOS << " unsigned OpIdx;\n"; + CvtOS << " Inst.setOpcode(Opcode);\n"; + CvtOS << " for (const uint8_t *p = Converter; *p; p+= 2) {\n"; + if (HasOptionalOperands) { + CvtOS << " OpIdx = *(p + 1) - NumDefaults;\n"; + } else { + CvtOS << " OpIdx = *(p + 1);\n"; + } + CvtOS << " switch (*p) {\n"; + CvtOS << " default: llvm_unreachable(\"invalid conversion entry!\");\n"; + CvtOS << " case CVT_Reg:\n"; + CvtOS << " static_cast<" << TargetOperandClass + << "&>(*Operands[OpIdx]).addRegOperands(Inst, 1);\n"; + CvtOS << " break;\n"; + CvtOS << " case CVT_Tied:\n"; + CvtOS << " Inst.addOperand(Inst.getOperand(OpIdx));\n"; + CvtOS << " break;\n"; std::string OperandFnBody; raw_string_ostream OpOS(OperandFnBody); @@ -1873,6 +1972,11 @@ static void emitConvertFuncs(CodeGenTarget &Target, StringRef ClassName, // the index of its entry in the vector). std::string Name = "CVT_" + (Op.Class->isRegisterClass() ? "Reg" : Op.Class->RenderMethod); + if (Op.Class->IsOptional) { + // For optional operands we must also care about DefaultMethod + assert(HasOptionalOperands); + Name += "_" + Op.Class->DefaultMethod; + } Name = getEnumNameForToken(Name); bool IsNewConverter = false; @@ -1888,11 +1992,27 @@ static void emitConvertFuncs(CodeGenTarget &Target, StringRef ClassName, // This is a new operand kind. Add a handler for it to the // converter driver. - CvtOS << " case " << Name << ":\n" - << " static_cast<" << TargetOperandClass - << "&>(*Operands[*(p + 1)])." << Op.Class->RenderMethod - << "(Inst, " << OpInfo.MINumOperands << ");\n" - << " break;\n"; + CvtOS << " case " << Name << ":\n"; + if (Op.Class->IsOptional) { + // If optional operand is not present in actual instruction then we + // should call its DefaultMethod before RenderMethod + assert(HasOptionalOperands); + CvtOS << " if (OptionalOperandsMask[*(p + 1) - 1]) {\n" + << " " << Op.Class->DefaultMethod << "()" + << "->" << Op.Class->RenderMethod << "(Inst, " + << OpInfo.MINumOperands << ");\n" + << " ++NumDefaults;\n" + << " } else {\n" + << " static_cast<" << TargetOperandClass + << "&>(*Operands[OpIdx])." << Op.Class->RenderMethod + << "(Inst, " << OpInfo.MINumOperands << ");\n" + << " }\n"; + } else { + CvtOS << " static_cast<" << TargetOperandClass + << "&>(*Operands[OpIdx])." << Op.Class->RenderMethod + << "(Inst, " << OpInfo.MINumOperands << ");\n"; + } + CvtOS << " break;\n"; // Add a handler for the operand number lookup. OpOS << " case " << Name << ":\n" @@ -2015,7 +2135,6 @@ static void emitConvertFuncs(CodeGenTarget &Target, StringRef ClassName, OS << " CVT_NUM_SIGNATURES\n"; OS << "};\n\n"; - OS << "} // end anonymous namespace\n\n"; // Output the conversion table. @@ -2051,6 +2170,7 @@ static void emitMatchClassEnumeration(CodeGenTarget &Target, << "/// instruction matching.\n"; OS << "enum MatchClassKind {\n"; OS << " InvalidMatchClass = 0,\n"; + OS << " OptionalMatchClass = 1,\n"; for (const auto &CI : Infos) { OS << " " << CI.Name << ", // "; if (CI.Kind == ClassInfo::Token) { @@ -2091,19 +2211,23 @@ static void emitValidateOperandClass(AsmMatcherInfo &Info, // Check the user classes. We don't care what order since we're only // actually matching against one of them. + OS << " switch (Kind) {\n" + " default: break;\n"; for (const auto &CI : Info.Classes) { if (!CI.isUserClass()) continue; OS << " // '" << CI.ClassName << "' class\n"; - OS << " if (Kind == " << CI.Name << ") {\n"; + OS << " case " << CI.Name << ":\n"; OS << " if (Operand." << CI.PredicateMethod << "())\n"; OS << " return MCTargetAsmParser::Match_Success;\n"; if (!CI.DiagnosticType.empty()) OS << " return " << Info.Target.getName() << "AsmParser::Match_" << CI.DiagnosticType << ";\n"; - OS << " }\n\n"; + else + OS << " break;\n"; } + OS << " } // end switch (Kind)\n\n"; // Check for register operands, including sub-classes. OS << " if (Operand.isReg()) {\n"; @@ -2137,6 +2261,8 @@ static void emitIsSubclass(CodeGenTarget &Target, bool EmittedSwitch = false; for (const auto &A : Infos) { std::vector<StringRef> SuperClasses; + if (A.IsOptional) + SuperClasses.push_back("OptionalMatchClass"); for (const auto &B : Infos) { if (&A != &B && A.isSubsetOf(B)) SuperClasses.push_back(B.Name); @@ -2225,6 +2351,37 @@ static void emitMatchRegisterName(CodeGenTarget &Target, Record *AsmParser, OS << "}\n\n"; } +/// Emit the function to match a string to the target +/// specific register enum. +static void emitMatchRegisterAltName(CodeGenTarget &Target, Record *AsmParser, + raw_ostream &OS) { + // Construct the match list. + std::vector<StringMatcher::StringPair> Matches; + const auto &Regs = Target.getRegBank().getRegisters(); + for (const CodeGenRegister &Reg : Regs) { + + auto AltNames = Reg.TheDef->getValueAsListOfStrings("AltNames"); + + for (auto AltName : AltNames) { + AltName = StringRef(AltName).trim(); + + // don't handle empty alternative names + if (AltName.empty()) + continue; + + Matches.emplace_back(AltName, + "return " + utostr(Reg.EnumValue) + ";"); + } + } + + OS << "static unsigned MatchRegisterAltName(StringRef Name) {\n"; + + StringMatcher("Name", Matches, OS).Emit(); + + OS << " return 0;\n"; + OS << "}\n\n"; +} + static const char *getMinimalTypeForRange(uint64_t Range) { assert(Range <= 0xFFFFFFFFFFFFFFFFULL && "Enum too large"); if (Range > 0xFFFFFFFFULL) @@ -2665,6 +2822,16 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { const std::unique_ptr<MatchableInfo> &b){ return *a < *b;}); +#ifndef NDEBUG + // Verify that the table is now sorted + for (auto I = Info.Matchables.begin(), E = Info.Matchables.end(); I != E; + ++I) { + for (auto J = I; J != E; ++J) { + assert(!(**J < **I)); + } + } +#endif // NDEBUG + DEBUG_WITH_TYPE("instruction_info", { for (const auto &MI : Info.Matchables) MI->dump(); @@ -2698,6 +2865,7 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { Info.buildOperandMatchInfo(); bool HasMnemonicFirst = AsmParser->getValueAsBit("HasMnemonicFirst"); + bool HasOptionalOperands = Info.hasOptionalOperands(); // Write the output. @@ -2707,10 +2875,16 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { OS << " // This should be included into the middle of the declaration of\n"; OS << " // your subclasses implementation of MCTargetAsmParser.\n"; OS << " uint64_t ComputeAvailableFeatures(const FeatureBitset& FB) const;\n"; - OS << " void convertToMCInst(unsigned Kind, MCInst &Inst, " - << "unsigned Opcode,\n" - << " const OperandVector " - << "&Operands);\n"; + if (HasOptionalOperands) { + OS << " void convertToMCInst(unsigned Kind, MCInst &Inst, " + << "unsigned Opcode,\n" + << " const OperandVector &Operands,\n" + << " const SmallBitVector &OptionalOperandsMask);\n"; + } else { + OS << " void convertToMCInst(unsigned Kind, MCInst &Inst, " + << "unsigned Opcode,\n" + << " const OperandVector &Operands);\n"; + } OS << " void convertToMapAndConstraints(unsigned Kind,\n "; OS << " const OperandVector &Operands) override;\n"; if (HasMnemonicFirst) @@ -2744,7 +2918,6 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { emitOperandDiagnosticTypes(Info, OS); OS << "#endif // GET_OPERAND_DIAGNOSTIC_TYPES\n\n"; - OS << "\n#ifdef GET_REGISTER_MATCHER\n"; OS << "#undef GET_REGISTER_MATCHER\n\n"; @@ -2756,6 +2929,9 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { if (AsmParser->getValueAsBit("ShouldEmitMatchRegisterName")) emitMatchRegisterName(Target, AsmParser, OS); + if (AsmParser->getValueAsBit("ShouldEmitMatchRegisterAltName")) + emitMatchRegisterAltName(Target, AsmParser, OS); + OS << "#endif // GET_REGISTER_MATCHER\n\n"; OS << "\n#ifdef GET_SUBTARGET_FEATURE_NAME\n"; @@ -2775,7 +2951,8 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { // Generate the convertToMCInst function to convert operands into an MCInst. // Also, generate the convertToMapAndConstraints function for MS-style inline // assembly. The latter doesn't actually generate a MCInst. - emitConvertFuncs(Target, ClassName, Info.Matchables, HasMnemonicFirst, OS); + emitConvertFuncs(Target, ClassName, Info.Matchables, HasMnemonicFirst, + HasOptionalOperands, OS); // Emit the enumeration for classes which participate in matching. emitMatchClassEnumeration(Target, Info.Classes, OS); @@ -2792,7 +2969,6 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { // Emit the available features compute function. emitComputeAvailableFeatures(Info, OS); - StringToOffsetTable StringTable; size_t MaxNumOperands = 0; @@ -2958,6 +3134,9 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { OS << " bool HadMatchOtherThanPredicate = false;\n"; OS << " unsigned RetCode = Match_InvalidOperand;\n"; OS << " uint64_t MissingFeatures = ~0ULL;\n"; + if (HasOptionalOperands) { + OS << " SmallBitVector OptionalOperandsMask(" << MaxNumOperands << ");\n"; + } OS << " // Set ErrorInfo to the operand that mismatches if it is\n"; OS << " // wrong for all instances of the instruction.\n"; OS << " ErrorInfo = ~0ULL;\n"; @@ -3002,36 +3181,55 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { // Emit check that the subclasses match. OS << " bool OperandsValid = true;\n"; - OS << " for (unsigned i = " << (HasMnemonicFirst ? "0" : "SIndex") - << "; i != " << MaxNumOperands << "; ++i) {\n"; - OS << " auto Formal = static_cast<MatchClassKind>(it->Classes[i]);\n"; - OS << " if (i" << (HasMnemonicFirst ? "+1" : "") - << " >= Operands.size()) {\n"; - OS << " OperandsValid = (Formal == " <<"InvalidMatchClass);\n"; - OS << " if (!OperandsValid) ErrorInfo = i" - << (HasMnemonicFirst ? "+1" : "") << ";\n"; + if (HasOptionalOperands) { + OS << " OptionalOperandsMask.reset(0, " << MaxNumOperands << ");\n"; + } + OS << " for (unsigned FormalIdx = " << (HasMnemonicFirst ? "0" : "SIndex") + << ", ActualIdx = " << (HasMnemonicFirst ? "1" : "SIndex") + << "; FormalIdx != " << MaxNumOperands << "; ++FormalIdx) {\n"; + OS << " auto Formal = " + << "static_cast<MatchClassKind>(it->Classes[FormalIdx]);\n"; + OS << " if (ActualIdx >= Operands.size()) {\n"; + OS << " OperandsValid = (Formal == " <<"InvalidMatchClass) || " + "isSubclass(Formal, OptionalMatchClass);\n"; + OS << " if (!OperandsValid) ErrorInfo = ActualIdx;\n"; + if (HasOptionalOperands) { + OS << " OptionalOperandsMask.set(FormalIdx, " << MaxNumOperands + << ");\n"; + } OS << " break;\n"; OS << " }\n"; - OS << " MCParsedAsmOperand &Actual = *Operands[i" - << (HasMnemonicFirst ? "+1" : "") << "];\n"; + OS << " MCParsedAsmOperand &Actual = *Operands[ActualIdx];\n"; OS << " unsigned Diag = validateOperandClass(Actual, Formal);\n"; - OS << " if (Diag == Match_Success)\n"; + OS << " if (Diag == Match_Success) {\n"; + OS << " ++ActualIdx;\n"; OS << " continue;\n"; + OS << " }\n"; OS << " // If the generic handler indicates an invalid operand\n"; OS << " // failure, check for a special case.\n"; OS << " if (Diag == Match_InvalidOperand) {\n"; OS << " Diag = validateTargetOperandClass(Actual, Formal);\n"; - OS << " if (Diag == Match_Success)\n"; + OS << " if (Diag == Match_Success) {\n"; + OS << " ++ActualIdx;\n"; OS << " continue;\n"; + OS << " }\n"; + OS << " }\n"; + OS << " // If current formal operand wasn't matched and it is optional\n" + << " // then try to match next formal operand\n"; + OS << " if (Diag == Match_InvalidOperand " + << "&& isSubclass(Formal, OptionalMatchClass)) {\n"; + if (HasOptionalOperands) { + OS << " OptionalOperandsMask.set(FormalIdx);\n"; + } + OS << " continue;\n"; OS << " }\n"; OS << " // If this operand is broken for all of the instances of this\n"; OS << " // mnemonic, keep track of it so we can report loc info.\n"; OS << " // If we already had a match that only failed due to a\n"; OS << " // target predicate, that diagnostic is preferred.\n"; OS << " if (!HadMatchOtherThanPredicate &&\n"; - OS << " (it == MnemonicRange.first || ErrorInfo <= i" - << (HasMnemonicFirst ? "+1" : "") << ")) {\n"; - OS << " ErrorInfo = i" << (HasMnemonicFirst ? "+1" : "") << ";\n"; + OS << " (it == MnemonicRange.first || ErrorInfo <= ActualIdx)) {\n"; + OS << " ErrorInfo = ActualIdx;\n"; OS << " // InvalidOperand is the default. Prefer specificity.\n"; OS << " if (Diag != Match_InvalidOperand)\n"; OS << " RetCode = Diag;\n"; @@ -3063,7 +3261,12 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { OS << " }\n\n"; OS << " // We have selected a definite instruction, convert the parsed\n" << " // operands into the appropriate MCInst.\n"; - OS << " convertToMCInst(it->ConvertFn, Inst, it->Opcode, Operands);\n"; + if (HasOptionalOperands) { + OS << " convertToMCInst(it->ConvertFn, Inst, it->Opcode, Operands,\n" + << " OptionalOperandsMask);\n"; + } else { + OS << " convertToMCInst(it->ConvertFn, Inst, it->Opcode, Operands);\n"; + } OS << "\n"; // Verify the instruction with the target-specific match predicate function. @@ -3118,4 +3321,4 @@ void EmitAsmMatcher(RecordKeeper &RK, raw_ostream &OS) { AsmMatcherEmitter(RK).run(OS); } -} // End llvm namespace +} // end namespace llvm diff --git a/utils/TableGen/AsmWriterEmitter.cpp b/utils/TableGen/AsmWriterEmitter.cpp index cf7cbd962865c..fc2138f7e8ea3 100644 --- a/utils/TableGen/AsmWriterEmitter.cpp +++ b/utils/TableGen/AsmWriterEmitter.cpp @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// // -// This tablegen backend is emits an assembly printer for the current target. +// This tablegen backend emits an assembly printer for the current target. // Note that this is currently fairly skeletal, but will grow over time. // //===----------------------------------------------------------------------===// @@ -27,6 +27,7 @@ #include <algorithm> #include <cassert> #include <map> +#include <utility> #include <vector> using namespace llvm; @@ -36,10 +37,8 @@ namespace { class AsmWriterEmitter { RecordKeeper &Records; CodeGenTarget Target; - std::map<const CodeGenInstruction*, AsmWriterInst*> CGIAWIMap; - const std::vector<const CodeGenInstruction*> *NumberedInstructions; + ArrayRef<const CodeGenInstruction *> NumberedInstructions; std::vector<AsmWriterInst> Instructions; - std::vector<std::string> PrintMethods; public: AsmWriterEmitter(RecordKeeper &R); @@ -50,21 +49,16 @@ private: void EmitGetRegisterName(raw_ostream &o); void EmitPrintAliasInstruction(raw_ostream &O); - AsmWriterInst *getAsmWriterInstByID(unsigned ID) const { - assert(ID < NumberedInstructions->size()); - std::map<const CodeGenInstruction*, AsmWriterInst*>::const_iterator I = - CGIAWIMap.find(NumberedInstructions->at(ID)); - assert(I != CGIAWIMap.end() && "Didn't find inst!"); - return I->second; - } void FindUniqueOperandCommands(std::vector<std::string> &UOC, - std::vector<unsigned> &InstIdxs, - std::vector<unsigned> &InstOpsUsed) const; + std::vector<std::vector<unsigned>> &InstIdxs, + std::vector<unsigned> &InstOpsUsed, + bool PassSubtarget) const; }; } // end anonymous namespace static void PrintCases(std::vector<std::pair<std::string, - AsmWriterOperand> > &OpsToPrint, raw_ostream &O) { + AsmWriterOperand> > &OpsToPrint, raw_ostream &O, + bool PassSubtarget) { O << " case " << OpsToPrint.back().first << ":"; AsmWriterOperand TheOp = OpsToPrint.back().second; OpsToPrint.pop_back(); @@ -78,7 +72,7 @@ static void PrintCases(std::vector<std::pair<std::string, } // Finally, emit the code. - O << "\n " << TheOp.getCode(); + O << "\n " << TheOp.getCode(PassSubtarget); O << "\n break;\n"; } @@ -86,7 +80,7 @@ static void PrintCases(std::vector<std::pair<std::string, /// EmitInstructions - Emit the last instruction in the vector and any other /// instructions that are suitably similar to it. static void EmitInstructions(std::vector<AsmWriterInst> &Insts, - raw_ostream &O) { + raw_ostream &O, bool PassSubtarget) { AsmWriterInst FirstInst = Insts.back(); Insts.pop_back(); @@ -115,7 +109,7 @@ static void EmitInstructions(std::vector<AsmWriterInst> &Insts, for (unsigned i = 0, e = FirstInst.Operands.size(); i != e; ++i) { if (i != DifferingOperand) { // If the operand is the same for all instructions, just print it. - O << " " << FirstInst.Operands[i].getCode(); + O << " " << FirstInst.Operands[i].getCode(PassSubtarget); } else { // If this is the operand that varies between all of the instructions, // emit a switch for just this operand now. @@ -133,7 +127,7 @@ static void EmitInstructions(std::vector<AsmWriterInst> &Insts, } std::reverse(OpsToPrint.begin(), OpsToPrint.end()); while (!OpsToPrint.empty()) - PrintCases(OpsToPrint, O); + PrintCases(OpsToPrint, O, PassSubtarget); O << " }"; } O << "\n"; @@ -143,9 +137,9 @@ static void EmitInstructions(std::vector<AsmWriterInst> &Insts, void AsmWriterEmitter:: FindUniqueOperandCommands(std::vector<std::string> &UniqueOperandCommands, - std::vector<unsigned> &InstIdxs, - std::vector<unsigned> &InstOpsUsed) const { - InstIdxs.assign(NumberedInstructions->size(), ~0U); + std::vector<std::vector<unsigned>> &InstIdxs, + std::vector<unsigned> &InstOpsUsed, + bool PassSubtarget) const { // This vector parallels UniqueOperandCommands, keeping track of which // instructions each case are used for. It is a comma separated string of @@ -154,31 +148,27 @@ FindUniqueOperandCommands(std::vector<std::string> &UniqueOperandCommands, InstrsForCase.resize(UniqueOperandCommands.size()); InstOpsUsed.assign(UniqueOperandCommands.size(), 0); - for (unsigned i = 0, e = NumberedInstructions->size(); i != e; ++i) { - const AsmWriterInst *Inst = getAsmWriterInstByID(i); - if (!Inst) - continue; // PHI, INLINEASM, CFI_INSTRUCTION, etc. - - if (Inst->Operands.empty()) + for (size_t i = 0, e = Instructions.size(); i != e; ++i) { + const AsmWriterInst &Inst = Instructions[i]; + if (Inst.Operands.empty()) continue; // Instruction already done. - std::string Command = " " + Inst->Operands[0].getCode() + "\n"; + std::string Command = " "+Inst.Operands[0].getCode(PassSubtarget)+"\n"; // Check to see if we already have 'Command' in UniqueOperandCommands. // If not, add it. - bool FoundIt = false; - for (unsigned idx = 0, e = UniqueOperandCommands.size(); idx != e; ++idx) - if (UniqueOperandCommands[idx] == Command) { - InstIdxs[i] = idx; - InstrsForCase[idx] += ", "; - InstrsForCase[idx] += Inst->CGI->TheDef->getName(); - FoundIt = true; - break; - } - if (!FoundIt) { - InstIdxs[i] = UniqueOperandCommands.size(); + auto I = std::find(UniqueOperandCommands.begin(), + UniqueOperandCommands.end(), Command); + if (I != UniqueOperandCommands.end()) { + size_t idx = I - UniqueOperandCommands.begin(); + InstrsForCase[idx] += ", "; + InstrsForCase[idx] += Inst.CGI->TheDef->getName(); + InstIdxs[idx].push_back(i); + } else { UniqueOperandCommands.push_back(std::move(Command)); - InstrsForCase.push_back(Inst->CGI->TheDef->getName()); + InstrsForCase.push_back(Inst.CGI->TheDef->getName()); + InstIdxs.emplace_back(); + InstIdxs.back().push_back(i); // This command matches one operand so far. InstOpsUsed.push_back(1); @@ -188,45 +178,33 @@ FindUniqueOperandCommands(std::vector<std::string> &UniqueOperandCommands, // For each entry of UniqueOperandCommands, there is a set of instructions // that uses it. If the next command of all instructions in the set are // identical, fold it into the command. - for (unsigned CommandIdx = 0, e = UniqueOperandCommands.size(); + for (size_t CommandIdx = 0, e = UniqueOperandCommands.size(); CommandIdx != e; ++CommandIdx) { - for (unsigned Op = 1; ; ++Op) { - // Scan for the first instruction in the set. - std::vector<unsigned>::iterator NIT = - std::find(InstIdxs.begin(), InstIdxs.end(), CommandIdx); - if (NIT == InstIdxs.end()) break; // No commonality. + const auto &Idxs = InstIdxs[CommandIdx]; + for (unsigned Op = 1; ; ++Op) { + // Find the first instruction in the set. + const AsmWriterInst &FirstInst = Instructions[Idxs.front()]; // If this instruction has no more operands, we isn't anything to merge // into this command. - const AsmWriterInst *FirstInst = - getAsmWriterInstByID(NIT-InstIdxs.begin()); - if (!FirstInst || FirstInst->Operands.size() == Op) + if (FirstInst.Operands.size() == Op) break; // Otherwise, scan to see if all of the other instructions in this command // set share the operand. - bool AllSame = true; - - for (NIT = std::find(NIT+1, InstIdxs.end(), CommandIdx); - NIT != InstIdxs.end(); - NIT = std::find(NIT+1, InstIdxs.end(), CommandIdx)) { - // Okay, found another instruction in this command set. If the operand - // matches, we're ok, otherwise bail out. - const AsmWriterInst *OtherInst = - getAsmWriterInstByID(NIT-InstIdxs.begin()); - - if (!OtherInst || OtherInst->Operands.size() == Op || - OtherInst->Operands[Op] != FirstInst->Operands[Op]) { - AllSame = false; - break; - } - } - if (!AllSame) break; + if (std::any_of(Idxs.begin()+1, Idxs.end(), + [&](unsigned Idx) { + const AsmWriterInst &OtherInst = Instructions[Idx]; + return OtherInst.Operands.size() == Op || + OtherInst.Operands[Op] != FirstInst.Operands[Op]; + })) + break; // Okay, everything in this command set has the same next operand. Add it // to UniqueOperandCommands and remember that it was consumed. - std::string Command = " " + FirstInst->Operands[Op].getCode() + "\n"; + std::string Command = " " + + FirstInst.Operands[Op].getCode(PassSubtarget) + "\n"; UniqueOperandCommands[CommandIdx] += Command; InstOpsUsed[CommandIdx]++; @@ -277,7 +255,7 @@ static void UnescapeString(std::string &Str) { void AsmWriterEmitter::EmitPrintInstruction(raw_ostream &O) { Record *AsmWriter = Target.getAsmWriter(); std::string ClassName = AsmWriter->getValueAsString("AsmWriterClassName"); - unsigned PassSubtarget = AsmWriter->getValueAsInt("PassSubtarget"); + bool PassSubtarget = AsmWriter->getValueAsInt("PassSubtarget"); O << "/// printInstruction - This method is automatically generated by tablegen\n" @@ -292,18 +270,16 @@ void AsmWriterEmitter::EmitPrintInstruction(raw_ostream &O) { /// OpcodeInfo - This encodes the index of the string to use for the first /// chunk of the output as well as indices used for operand printing. - std::vector<uint64_t> OpcodeInfo; + std::vector<uint64_t> OpcodeInfo(NumberedInstructions.size()); const unsigned OpcodeInfoBits = 64; // Add all strings to the string table upfront so it can generate an optimized // representation. - for (const CodeGenInstruction *Inst : *NumberedInstructions) { - AsmWriterInst *AWI = CGIAWIMap[Inst]; - if (AWI && - AWI->Operands[0].OperandType == + for (AsmWriterInst &AWI : Instructions) { + if (AWI.Operands[0].OperandType == AsmWriterOperand::isLiteralTextOperand && - !AWI->Operands[0].Str.empty()) { - std::string Str = AWI->Operands[0].Str; + !AWI.Operands[0].Str.empty()) { + std::string Str = AWI.Operands[0].Str; UnescapeString(Str); StringTable.add(Str); } @@ -312,29 +288,24 @@ void AsmWriterEmitter::EmitPrintInstruction(raw_ostream &O) { StringTable.layout(); unsigned MaxStringIdx = 0; - for (const CodeGenInstruction *Inst : *NumberedInstructions) { - AsmWriterInst *AWI = CGIAWIMap[Inst]; + for (AsmWriterInst &AWI : Instructions) { unsigned Idx; - if (!AWI) { - // Something not handled by the asmwriter printer. - Idx = ~0U; - } else if (AWI->Operands[0].OperandType != - AsmWriterOperand::isLiteralTextOperand || - AWI->Operands[0].Str.empty()) { + if (AWI.Operands[0].OperandType != AsmWriterOperand::isLiteralTextOperand || + AWI.Operands[0].Str.empty()) { // Something handled by the asmwriter printer, but with no leading string. Idx = StringTable.get(""); } else { - std::string Str = AWI->Operands[0].Str; + std::string Str = AWI.Operands[0].Str; UnescapeString(Str); Idx = StringTable.get(Str); MaxStringIdx = std::max(MaxStringIdx, Idx); // Nuke the string from the operand list. It is now handled! - AWI->Operands.erase(AWI->Operands.begin()); + AWI.Operands.erase(AWI.Operands.begin()); } // Bias offset by one since we want 0 as a sentinel. - OpcodeInfo.push_back(Idx+1); + OpcodeInfo[AWI.CGIIndex] = Idx+1; } // Figure out how many bits we used for the string index. @@ -348,10 +319,10 @@ void AsmWriterEmitter::EmitPrintInstruction(raw_ostream &O) { while (1) { std::vector<std::string> UniqueOperandCommands; - std::vector<unsigned> InstIdxs; + std::vector<std::vector<unsigned>> InstIdxs; std::vector<unsigned> NumInstOpsHandled; FindUniqueOperandCommands(UniqueOperandCommands, InstIdxs, - NumInstOpsHandled); + NumInstOpsHandled, PassSubtarget); // If we ran out of operands to print, we're done. if (UniqueOperandCommands.empty()) break; @@ -368,23 +339,22 @@ void AsmWriterEmitter::EmitPrintInstruction(raw_ostream &O) { } // Otherwise, we can include this in the initial lookup table. Add it in. - for (unsigned i = 0, e = InstIdxs.size(); i != e; ++i) - if (InstIdxs[i] != ~0U) { - OpcodeInfo[i] |= (uint64_t)InstIdxs[i] << (OpcodeInfoBits-BitsLeft); - } - BitsLeft -= NumBits; - - // Remove the info about this operand. - for (unsigned i = 0, e = NumberedInstructions->size(); i != e; ++i) { - if (AsmWriterInst *Inst = getAsmWriterInstByID(i)) - if (!Inst->Operands.empty()) { - unsigned NumOps = NumInstOpsHandled[InstIdxs[i]]; - assert(NumOps <= Inst->Operands.size() && + for (size_t i = 0, e = InstIdxs.size(); i != e; ++i) { + unsigned NumOps = NumInstOpsHandled[i]; + for (unsigned Idx : InstIdxs[i]) { + OpcodeInfo[Instructions[Idx].CGIIndex] |= + (uint64_t)i << (OpcodeInfoBits-BitsLeft); + // Remove the info about this operand from the instruction. + AsmWriterInst &Inst = Instructions[Idx]; + if (!Inst.Operands.empty()) { + assert(NumOps <= Inst.Operands.size() && "Can't remove this many ops!"); - Inst->Operands.erase(Inst->Operands.begin(), - Inst->Operands.begin()+NumOps); + Inst.Operands.erase(Inst.Operands.begin(), + Inst.Operands.begin()+NumOps); } + } } + BitsLeft -= NumBits; // Remember the handlers for this set of operands. TableDrivenOperandPrinters.push_back(std::move(UniqueOperandCommands)); @@ -411,9 +381,9 @@ void AsmWriterEmitter::EmitPrintInstruction(raw_ostream &O) { uint64_t Mask = (1ULL << TableSize) - 1; O << " static const uint" << TableSize << "_t OpInfo" << Table << "[] = {\n"; - for (unsigned i = 0, e = NumberedInstructions->size(); i != e; ++i) { + for (unsigned i = 0, e = NumberedInstructions.size(); i != e; ++i) { O << " " << ((OpcodeInfo[i] >> Shift) & Mask) << "U,\t// " - << NumberedInstructions->at(i)->TheDef->getName() << "\n"; + << NumberedInstructions[i]->TheDef->getName() << "\n"; } O << " };\n\n"; // Emit string to combine the individual table lookups. @@ -502,7 +472,7 @@ void AsmWriterEmitter::EmitPrintInstruction(raw_ostream &O) { O << " switch (MI->getOpcode()) {\n"; O << " default: llvm_unreachable(\"Unexpected opcode.\");\n"; while (!Instructions.empty()) - EmitInstructions(Instructions, O); + EmitInstructions(Instructions, O, PassSubtarget); O << " }\n"; } @@ -578,7 +548,7 @@ void AsmWriterEmitter::EmitGetRegisterName(raw_ostream &O) { Record *AsmWriter = Target.getAsmWriter(); std::string ClassName = AsmWriter->getValueAsString("AsmWriterClassName"); const auto &Registers = Target.getRegBank().getRegisters(); - std::vector<Record*> AltNameIndices = Target.getRegAltNameIndices(); + const std::vector<Record*> &AltNameIndices = Target.getRegAltNameIndices(); bool hasAltNames = AltNameIndices.size() > 1; std::string Namespace = Registers.front().TheDef->getValueAsString("Namespace"); @@ -606,7 +576,7 @@ void AsmWriterEmitter::EmitGetRegisterName(raw_ostream &O) { O << " switch(AltIdx) {\n" << " default: llvm_unreachable(\"Invalid register alt name index!\");\n"; for (const Record *R : AltNameIndices) { - std::string AltName(R->getName()); + const std::string &AltName = R->getName(); std::string Prefix = !Namespace.empty() ? Namespace + "::" : ""; O << " case " << Prefix << AltName << ":\n" << " assert(*(AsmStrs" << AltName << "+RegAsmOffset" @@ -631,12 +601,12 @@ namespace { class IAPrinter { std::vector<std::string> Conds; std::map<StringRef, std::pair<int, int>> OpMap; - SmallVector<Record*, 4> ReqFeatures; std::string Result; std::string AsmString; public: - IAPrinter(std::string R, std::string AS) : Result(R), AsmString(AS) {} + IAPrinter(std::string R, std::string AS) + : Result(std::move(R)), AsmString(std::move(AS)) {} void addCond(const std::string &C) { Conds.push_back(C); } @@ -677,7 +647,7 @@ public: } void print(raw_ostream &O) { - if (Conds.empty() && ReqFeatures.empty()) { + if (Conds.empty()) { O.indent(6) << "return true;\n"; return; } @@ -784,7 +754,7 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) { // Emit the method that prints the alias instruction. std::string ClassName = AsmWriter->getValueAsString("AsmWriterClassName"); unsigned Variant = AsmWriter->getValueAsInt("Variant"); - unsigned PassSubtarget = AsmWriter->getValueAsInt("PassSubtarget"); + bool PassSubtarget = AsmWriter->getValueAsInt("PassSubtarget"); std::vector<Record*> AllInstAliases = Records.getAllDerivedDefinitions("InstAlias"); @@ -808,6 +778,8 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) { // before it can be matched to the mnemonic. std::map<std::string, std::vector<IAPrinter>> IAPrinterMap; + std::vector<std::string> PrintMethods; + // A list of MCOperandPredicates for all operands in use, and the reverse map std::vector<const Record*> MCOpPredicates; DenseMap<const Record*, unsigned> MCOpPredicateMap; @@ -825,6 +797,18 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) { IAPrinter IAP(CGA.Result->getAsString(), CGA.AsmString); + std::string Namespace = Target.getName(); + std::vector<Record *> ReqFeatures; + if (PassSubtarget) { + // We only consider ReqFeatures predicates if PassSubtarget + std::vector<Record *> RF = + CGA.TheDef->getValueAsListOfDefs("Predicates"); + std::copy_if(RF.begin(), RF.end(), std::back_inserter(ReqFeatures), + [](Record *R) { + return R->getValueAsBit("AssemblerMatcherPredicate"); + }); + } + unsigned NumMIOps = 0; for (auto &Operand : CGA.ResultOperands) NumMIOps += Operand.getMINumOperands(); @@ -929,6 +913,27 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) { } if (CantHandle) continue; + + for (auto I = ReqFeatures.cbegin(); I != ReqFeatures.cend(); I++) { + Record *R = *I; + std::string AsmCondString = R->getValueAsString("AssemblerCondString"); + + // AsmCondString has syntax [!]F(,[!]F)* + SmallVector<StringRef, 4> Ops; + SplitString(AsmCondString, Ops, ","); + assert(!Ops.empty() && "AssemblerCondString cannot be empty"); + + for (auto &Op : Ops) { + assert(!Op.empty() && "Empty operator"); + if (Op[0] == '!') + Cond = "!STI.getFeatureBits()[" + Namespace + "::" + + Op.substr(1).str() + "]"; + else + Cond = "STI.getFeatureBits()[" + Namespace + "::" + Op.str() + "]"; + IAP.addCond(Cond); + } + } + IAPrinterMap[Aliases.first].push_back(std::move(IAP)); } } @@ -1002,13 +1007,14 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) { // Code that prints the alias, replacing the operands with the ones from the // MCInst. O << " unsigned I = 0;\n"; - O << " while (AsmString[I] != ' ' && AsmString[I] != '\t' &&\n"; - O << " AsmString[I] != '\\0')\n"; + O << " while (AsmString[I] != ' ' && AsmString[I] != '\\t' &&\n"; + O << " AsmString[I] != '$' && AsmString[I] != '\\0')\n"; O << " ++I;\n"; O << " OS << '\\t' << StringRef(AsmString, I);\n"; O << " if (AsmString[I] != '\\0') {\n"; - O << " OS << '\\t';\n"; + O << " if (AsmString[I] == ' ' || AsmString[I] == '\\t')"; + O << " OS << '\\t';\n"; O << " do {\n"; O << " if (AsmString[I] == '$') {\n"; O << " ++I;\n"; @@ -1072,7 +1078,7 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) { for (unsigned i = 0; i < MCOpPredicates.size(); ++i) { Init *MCOpPred = MCOpPredicates[i]->getValueInit("MCOperandPredicate"); - if (StringInit *SI = dyn_cast<StringInit>(MCOpPred)) { + if (CodeInit *SI = dyn_cast<CodeInit>(MCOpPred)) { O << " case " << i + 1 << ": {\n" << SI->getValue() << "\n" << " }\n"; @@ -1089,19 +1095,15 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) { AsmWriterEmitter::AsmWriterEmitter(RecordKeeper &R) : Records(R), Target(R) { Record *AsmWriter = Target.getAsmWriter(); unsigned Variant = AsmWriter->getValueAsInt("Variant"); - unsigned PassSubtarget = AsmWriter->getValueAsInt("PassSubtarget"); - for (const CodeGenInstruction *I : Target.instructions()) - if (!I->AsmString.empty() && I->TheDef->getName() != "PHI") - Instructions.emplace_back(*I, Variant, PassSubtarget); // Get the instruction numbering. - NumberedInstructions = &Target.getInstructionsByEnumValue(); + NumberedInstructions = Target.getInstructionsByEnumValue(); - // Compute the CodeGenInstruction -> AsmWriterInst mapping. Note that not - // all machine instructions are necessarily being printed, so there may be - // target instructions not in this map. - for (AsmWriterInst &AWI : Instructions) - CGIAWIMap.insert(std::make_pair(AWI.CGI, &AWI)); + for (unsigned i = 0, e = NumberedInstructions.size(); i != e; ++i) { + const CodeGenInstruction *I = NumberedInstructions[i]; + if (!I->AsmString.empty() && I->TheDef->getName() != "PHI") + Instructions.emplace_back(*I, i, Variant); + } } void AsmWriterEmitter::run(raw_ostream &O) { diff --git a/utils/TableGen/AsmWriterInst.cpp b/utils/TableGen/AsmWriterInst.cpp index 5b09765a2756b..2c19e5d663d66 100644 --- a/utils/TableGen/AsmWriterInst.cpp +++ b/utils/TableGen/AsmWriterInst.cpp @@ -26,7 +26,7 @@ static bool isIdentChar(char C) { C == '_'; } -std::string AsmWriterOperand::getCode() const { +std::string AsmWriterOperand::getCode(bool PassSubtarget) const { if (OperandType == isLiteralTextOperand) { if (Str.size() == 1) return "O << '" + Str + "';"; @@ -50,9 +50,9 @@ std::string AsmWriterOperand::getCode() const { /// ParseAsmString - Parse the specified Instruction's AsmString into this /// AsmWriterInst. /// -AsmWriterInst::AsmWriterInst(const CodeGenInstruction &CGI, unsigned Variant, - unsigned PassSubtarget) { - this->CGI = &CGI; +AsmWriterInst::AsmWriterInst(const CodeGenInstruction &CGI, unsigned CGIIndex, + unsigned Variant) + : CGI(&CGI), CGIIndex(CGIIndex) { // NOTE: Any extensions to this code need to be mirrored in the // AsmPrinter::printInlineAsm code that executes as compile time (assuming @@ -120,8 +120,7 @@ AsmWriterInst::AsmWriterInst(const CodeGenInstruction &CGI, unsigned Variant, while (VarEnd < AsmString.size() && isIdentChar(AsmString[VarEnd])) ++VarEnd; - std::string VarName(AsmString.begin()+DollarPos+1, - AsmString.begin()+VarEnd); + StringRef VarName(AsmString.data()+DollarPos+1, VarEnd-DollarPos-1); // Modifier - Support ${foo:modifier} syntax, where "modifier" is passed // into printOperand. Also support ${:feature}, which is passed into @@ -143,7 +142,7 @@ AsmWriterInst::AsmWriterInst(const CodeGenInstruction &CGI, unsigned Variant, PrintFatalError("Reached end of string before terminating curly brace in '" + CGI.TheDef->getName() + "'"); - unsigned ModifierStart = VarEnd; + std::string::size_type ModifierStart = VarEnd; while (VarEnd < AsmString.size() && isIdentChar(AsmString[VarEnd])) ++VarEnd; Modifier = std::string(AsmString.begin()+ModifierStart, @@ -163,16 +162,14 @@ AsmWriterInst::AsmWriterInst(const CodeGenInstruction &CGI, unsigned Variant, if (VarName.empty()) { // Just a modifier, pass this into PrintSpecial. - Operands.emplace_back("PrintSpecial", ~0U, ~0U, Modifier, - PassSubtarget); + Operands.emplace_back("PrintSpecial", ~0U, Modifier); } else { // Otherwise, normal operand. unsigned OpNo = CGI.Operands.getOperandNamed(VarName); CGIOperandList::OperandInfo OpInfo = CGI.Operands[OpNo]; unsigned MIOp = OpInfo.MIOperandNo; - Operands.emplace_back(OpInfo.PrinterMethodName, OpNo, MIOp, Modifier, - PassSubtarget); + Operands.emplace_back(OpInfo.PrinterMethodName, MIOp, Modifier); } LastEmitted = VarEnd; } diff --git a/utils/TableGen/AsmWriterInst.h b/utils/TableGen/AsmWriterInst.h index a597e6ba1a558..708f23cb5b0ef 100644 --- a/utils/TableGen/AsmWriterInst.h +++ b/utils/TableGen/AsmWriterInst.h @@ -35,29 +35,20 @@ namespace llvm { isLiteralStatementOperand } OperandType; + /// MiOpNo - For isMachineInstrOperand, this is the operand number of the + /// machine instruction. + unsigned MIOpNo; + /// Str - For isLiteralTextOperand, this IS the literal text. For /// isMachineInstrOperand, this is the PrinterMethodName for the operand.. /// For isLiteralStatementOperand, this is the code to insert verbatim /// into the asm writer. std::string Str; - /// CGIOpNo - For isMachineInstrOperand, this is the index of the operand in - /// the CodeGenInstruction. - unsigned CGIOpNo; - - /// MiOpNo - For isMachineInstrOperand, this is the operand number of the - /// machine instruction. - unsigned MIOpNo; - /// MiModifier - For isMachineInstrOperand, this is the modifier string for /// an operand, specified with syntax like ${opname:modifier}. std::string MiModifier; - // PassSubtarget - Pass MCSubtargetInfo to the print method if this is - // equal to 1. - // FIXME: Remove after all ports are updated. - unsigned PassSubtarget; - // To make VS STL happy AsmWriterOperand(OpType op = isLiteralTextOperand):OperandType(op) {} @@ -66,13 +57,10 @@ namespace llvm { : OperandType(op), Str(LitStr) {} AsmWriterOperand(const std::string &Printer, - unsigned _CGIOpNo, unsigned _MIOpNo, const std::string &Modifier, - unsigned PassSubtarget, OpType op = isMachineInstrOperand) - : OperandType(op), Str(Printer), CGIOpNo(_CGIOpNo), MIOpNo(_MIOpNo), - MiModifier(Modifier), PassSubtarget(PassSubtarget) {} + : OperandType(op), MIOpNo(_MIOpNo), Str(Printer), MiModifier(Modifier) {} bool operator!=(const AsmWriterOperand &Other) const { if (OperandType != Other.OperandType || Str != Other.Str) return true; @@ -85,16 +73,17 @@ namespace llvm { } /// getCode - Return the code that prints this operand. - std::string getCode() const; + std::string getCode(bool PassSubtarget) const; }; class AsmWriterInst { public: std::vector<AsmWriterOperand> Operands; const CodeGenInstruction *CGI; + unsigned CGIIndex; - AsmWriterInst(const CodeGenInstruction &CGI, - unsigned Variant, unsigned PassSubtarget); + AsmWriterInst(const CodeGenInstruction &CGI, unsigned CGIIndex, + unsigned Variant); /// MatchesAllButOneOp - If this instruction is exactly identical to the /// specified instruction except for one differing operand, return the diff --git a/utils/TableGen/Attributes.cpp b/utils/TableGen/Attributes.cpp index 7b001bf14de58..58dbe5767adab 100644 --- a/utils/TableGen/Attributes.cpp +++ b/utils/TableGen/Attributes.cpp @@ -27,6 +27,7 @@ public: private: void emitTargetIndependentEnums(raw_ostream &OS); + void emitConversionFn(raw_ostream &OS); void emitFnAttrCompatCheck(raw_ostream &OS, bool IsStringAttr); void printEnumAttrClasses(raw_ostream &OS, @@ -52,6 +53,27 @@ void Attributes::emitTargetIndependentEnums(raw_ostream &OS) { OS << "#endif\n"; } +void Attributes::emitConversionFn(raw_ostream &OS) { + OS << "#ifdef GET_ATTR_KIND_FROM_NAME\n"; + OS << "#undef GET_ATTR_KIND_FROM_NAME\n"; + + std::vector<Record*> Attrs = + Records.getAllDerivedDefinitions("EnumAttr"); + + OS << "static Attribute::AttrKind getAttrKindFromName(StringRef AttrName) {\n"; + OS << " return StringSwitch<Attribute::AttrKind>(AttrName)\n"; + + for (auto A : Attrs) { + OS << " .Case(\"" << A->getValueAsString("AttrString"); + OS << "\", Attribute::" << A->getName() << ")\n"; + } + + OS << " .Default(Attribute::None);\n"; + OS << "}\n\n"; + + OS << "#endif\n"; +} + void Attributes::emitFnAttrCompatCheck(raw_ostream &OS, bool IsStringAttr) { OS << "#ifdef GET_ATTR_COMPAT_FUNC\n"; OS << "#undef GET_ATTR_COMPAT_FUNC\n"; @@ -144,6 +166,7 @@ void Attributes::printStrBoolAttrClasses(raw_ostream &OS, void Attributes::emit(raw_ostream &OS) { emitTargetIndependentEnums(OS); + emitConversionFn(OS); emitFnAttrCompatCheck(OS, false); } diff --git a/utils/TableGen/CMakeLists.txt b/utils/TableGen/CMakeLists.txt index eef1540424dd1..e8fe30f5ac704 100644 --- a/utils/TableGen/CMakeLists.txt +++ b/utils/TableGen/CMakeLists.txt @@ -27,6 +27,7 @@ add_tablegen(llvm-tblgen LLVM OptParserEmitter.cpp PseudoLoweringEmitter.cpp RegisterInfoEmitter.cpp + SearchableTableEmitter.cpp SubtargetEmitter.cpp TableGen.cpp X86DisassemblerTables.cpp diff --git a/utils/TableGen/CodeEmitterGen.cpp b/utils/TableGen/CodeEmitterGen.cpp index 46fcdf5e96ffd..400913e327459 100644 --- a/utils/TableGen/CodeEmitterGen.cpp +++ b/utils/TableGen/CodeEmitterGen.cpp @@ -15,7 +15,6 @@ #include "CodeGenTarget.h" #include "llvm/ADT/StringExtras.h" -#include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/TableGen/Record.h" #include "llvm/TableGen/TableGenBackend.h" @@ -227,7 +226,7 @@ void CodeEmitterGen::run(raw_ostream &o) { // For little-endian instruction bit encodings, reverse the bit order Target.reverseBitsForLittleEndianEncoding(); - const std::vector<const CodeGenInstruction*> &NumberedInstructions = + ArrayRef<const CodeGenInstruction*> NumberedInstructions = Target.getInstructionsByEnumValue(); // Emit function declaration @@ -238,11 +237,7 @@ void CodeEmitterGen::run(raw_ostream &o) { // Emit instruction base values o << " static const uint64_t InstBits[] = {\n"; - for (std::vector<const CodeGenInstruction*>::const_iterator - IN = NumberedInstructions.begin(), - EN = NumberedInstructions.end(); - IN != EN; ++IN) { - const CodeGenInstruction *CGI = *IN; + for (const CodeGenInstruction *CGI : NumberedInstructions) { Record *R = CGI->TheDef; if (R->getValueAsString("Namespace") == "TargetOpcode" || diff --git a/utils/TableGen/CodeGenDAGPatterns.cpp b/utils/TableGen/CodeGenDAGPatterns.cpp index 3ebe51e05121d..307a95373c9d0 100644 --- a/utils/TableGen/CodeGenDAGPatterns.cpp +++ b/utils/TableGen/CodeGenDAGPatterns.cpp @@ -2392,8 +2392,8 @@ void TreePattern::dump() const { print(errs()); } CodeGenDAGPatterns::CodeGenDAGPatterns(RecordKeeper &R) : Records(R), Target(R) { - Intrinsics = LoadIntrinsics(Records, false); - TgtIntrinsics = LoadIntrinsics(Records, true); + Intrinsics = CodeGenIntrinsicTable(Records, false); + TgtIntrinsics = CodeGenIntrinsicTable(Records, true); ParseNodeInfo(); ParseNodeTransforms(); ParseComplexPatterns(); @@ -2816,14 +2816,14 @@ public: if (const CodeGenIntrinsic *IntInfo = N->getIntrinsicInfo(CDP)) { // If this is an intrinsic, analyze it. - if (IntInfo->ModRef >= CodeGenIntrinsic::ReadArgMem) + if (IntInfo->ModRef & CodeGenIntrinsic::MR_Ref) mayLoad = true;// These may load memory. - if (IntInfo->ModRef >= CodeGenIntrinsic::ReadWriteArgMem) + if (IntInfo->ModRef & CodeGenIntrinsic::MR_Mod) mayStore = true;// Intrinsics that can write to memory are 'mayStore'. if (IntInfo->ModRef >= CodeGenIntrinsic::ReadWriteMem) - // WriteMem intrinsics can have other strange effects. + // ReadWriteMem intrinsics can have other strange effects. hasSideEffects = true; } } @@ -2974,9 +2974,16 @@ const DAGInstruction &CodeGenDAGPatterns::parseInstructionPattern( // fill in the InstResults map. for (unsigned j = 0, e = I->getNumTrees(); j != e; ++j) { TreePatternNode *Pat = I->getTree(j); - if (Pat->getNumTypes() != 0) + if (Pat->getNumTypes() != 0) { + std::string Types; + for (unsigned k = 0, ke = Pat->getNumTypes(); k != ke; ++k) { + if (k > 0) + Types += ", "; + Types += Pat->getExtType(k).getName(); + } I->error("Top-level forms in instruction pattern should have" - " void types"); + " void types, has types " + Types); + } // Find inputs and outputs, and verify the structure of the uses/defs. FindPatternInputsAndOutputs(I, Pat, InstInputs, InstResults, @@ -3249,7 +3256,7 @@ void CodeGenDAGPatterns::AddPatternToMatch(TreePattern *Pattern, void CodeGenDAGPatterns::InferInstructionFlags() { - const std::vector<const CodeGenInstruction*> &Instructions = + ArrayRef<const CodeGenInstruction*> Instructions = Target.getInstructionsByEnumValue(); // First try to infer flags from the primary instruction pattern, if any. diff --git a/utils/TableGen/CodeGenDAGPatterns.h b/utils/TableGen/CodeGenDAGPatterns.h index 76c9cefea50fd..819c4b8492cb2 100644 --- a/utils/TableGen/CodeGenDAGPatterns.h +++ b/utils/TableGen/CodeGenDAGPatterns.h @@ -716,8 +716,8 @@ public: class CodeGenDAGPatterns { RecordKeeper &Records; CodeGenTarget Target; - std::vector<CodeGenIntrinsic> Intrinsics; - std::vector<CodeGenIntrinsic> TgtIntrinsics; + CodeGenIntrinsicTable Intrinsics; + CodeGenIntrinsicTable TgtIntrinsics; std::map<Record*, SDNodeInfo, LessRecordByID> SDNodes; std::map<Record*, std::pair<Record*, std::string>, LessRecordByID> SDNodeXForms; diff --git a/utils/TableGen/CodeGenInstruction.cpp b/utils/TableGen/CodeGenInstruction.cpp index 366e8ec7fac15..ec802363030e7 100644 --- a/utils/TableGen/CodeGenInstruction.cpp +++ b/utils/TableGen/CodeGenInstruction.cpp @@ -49,7 +49,9 @@ CGIOperandList::CGIOperandList(Record *R) : TheDef(R) { unsigned MIOperandNo = 0; std::set<std::string> OperandNames; - for (unsigned i = 0, e = InDI->getNumArgs()+OutDI->getNumArgs(); i != e; ++i){ + unsigned e = InDI->getNumArgs() + OutDI->getNumArgs(); + OperandList.reserve(e); + for (unsigned i = 0; i != e; ++i){ Init *ArgInit; std::string ArgName; if (i < NumDefs) { @@ -322,6 +324,7 @@ CodeGenInstruction::CodeGenInstruction(Record *R) isExtractSubreg = R->getValueAsBit("isExtractSubreg"); isInsertSubreg = R->getValueAsBit("isInsertSubreg"); isConvergent = R->getValueAsBit("isConvergent"); + hasNoSchedulingInfo = R->getValueAsBit("hasNoSchedulingInfo"); bool Unset; mayLoad = R->getValueAsBitOrUnset("mayLoad", Unset); diff --git a/utils/TableGen/CodeGenInstruction.h b/utils/TableGen/CodeGenInstruction.h index 8f01abd5403cf..8e5a03d7b7439 100644 --- a/utils/TableGen/CodeGenInstruction.h +++ b/utils/TableGen/CodeGenInstruction.h @@ -14,7 +14,6 @@ #ifndef LLVM_UTILS_TABLEGEN_CODEGENINSTRUCTION_H #define LLVM_UTILS_TABLEGEN_CODEGENINSTRUCTION_H -#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/CodeGen/MachineValueType.h" #include "llvm/Support/SMLoc.h" @@ -23,10 +22,10 @@ #include <vector> namespace llvm { +template <typename T> class ArrayRef; class Record; class DagInit; class CodeGenTarget; - class StringRef; class CGIOperandList { public: @@ -257,6 +256,7 @@ namespace llvm { bool isExtractSubreg : 1; bool isInsertSubreg : 1; bool isConvergent : 1; + bool hasNoSchedulingInfo : 1; std::string DeprecatedReason; bool HasComplexDeprecationPredicate; @@ -316,7 +316,8 @@ namespace llvm { K_Reg } Kind; - ResultOperand(std::string N, Record *r) : Name(N), R(r), Kind(K_Record) {} + ResultOperand(std::string N, Record *r) + : Name(std::move(N)), R(r), Kind(K_Record) {} ResultOperand(int64_t I) : Imm(I), Kind(K_Imm) {} ResultOperand(Record *r) : R(r), Kind(K_Reg) {} diff --git a/utils/TableGen/CodeGenIntrinsics.h b/utils/TableGen/CodeGenIntrinsics.h index 7bdb7e1bc537c..ea3ec67d62edd 100644 --- a/utils/TableGen/CodeGenIntrinsics.h +++ b/utils/TableGen/CodeGenIntrinsics.h @@ -19,85 +19,122 @@ #include <vector> namespace llvm { - class Record; - class RecordKeeper; - class CodeGenTarget; - - struct CodeGenIntrinsic { - Record *TheDef; // The actual record defining this intrinsic. - std::string Name; // The name of the LLVM function "llvm.bswap.i32" - std::string EnumName; // The name of the enum "bswap_i32" - std::string GCCBuiltinName;// Name of the corresponding GCC builtin, or "". - std::string MSBuiltinName; // Name of the corresponding MS builtin, or "". - std::string TargetPrefix; // Target prefix, e.g. "ppc" for t-s intrinsics. - - /// IntrinsicSignature - This structure holds the return values and - /// parameter values of an intrinsic. If the number of return values is > 1, - /// then the intrinsic implicitly returns a first-class aggregate. The - /// numbering of the types starts at 0 with the first return value and - /// continues from there through the parameter list. This is useful for - /// "matching" types. - struct IntrinsicSignature { - /// RetVTs - The MVT::SimpleValueType for each return type. Note that this - /// list is only populated when in the context of a target .td file. When - /// building Intrinsics.td, this isn't available, because we don't know - /// the target pointer size. - std::vector<MVT::SimpleValueType> RetVTs; - - /// RetTypeDefs - The records for each return type. - std::vector<Record*> RetTypeDefs; - - /// ParamVTs - The MVT::SimpleValueType for each parameter type. Note that - /// this list is only populated when in the context of a target .td file. - /// When building Intrinsics.td, this isn't available, because we don't - /// know the target pointer size. - std::vector<MVT::SimpleValueType> ParamVTs; - - /// ParamTypeDefs - The records for each parameter type. - std::vector<Record*> ParamTypeDefs; - }; - - IntrinsicSignature IS; - - // Memory mod/ref behavior of this intrinsic. - enum ModRefKind { - NoMem, ReadArgMem, ReadMem, ReadWriteArgMem, ReadWriteMem - }; - ModRefKind ModRef; - - /// This is set to true if the intrinsic is overloaded by its argument - /// types. - bool isOverloaded; - - /// isCommutative - True if the intrinsic is commutative. - bool isCommutative; - - /// canThrow - True if the intrinsic can throw. - bool canThrow; - - /// isNoDuplicate - True if the intrinsic is marked as noduplicate. - bool isNoDuplicate; - - /// isNoReturn - True if the intrinsic is no-return. - bool isNoReturn; - - /// isConvergent - True if the intrinsic is marked as convergent. - bool isConvergent; - - enum ArgAttribute { - NoCapture, - ReadOnly, - ReadNone - }; - std::vector<std::pair<unsigned, ArgAttribute> > ArgumentAttributes; - - CodeGenIntrinsic(Record *R); +class Record; +class RecordKeeper; +class CodeGenTarget; + +struct CodeGenIntrinsic { + Record *TheDef; // The actual record defining this intrinsic. + std::string Name; // The name of the LLVM function "llvm.bswap.i32" + std::string EnumName; // The name of the enum "bswap_i32" + std::string GCCBuiltinName; // Name of the corresponding GCC builtin, or "". + std::string MSBuiltinName; // Name of the corresponding MS builtin, or "". + std::string TargetPrefix; // Target prefix, e.g. "ppc" for t-s intrinsics. + + /// This structure holds the return values and parameter values of an + /// intrinsic. If the number of return values is > 1, then the intrinsic + /// implicitly returns a first-class aggregate. The numbering of the types + /// starts at 0 with the first return value and continues from there through + /// the parameter list. This is useful for "matching" types. + struct IntrinsicSignature { + /// The MVT::SimpleValueType for each return type. Note that this list is + /// only populated when in the context of a target .td file. When building + /// Intrinsics.td, this isn't available, because we don't know the target + /// pointer size. + std::vector<MVT::SimpleValueType> RetVTs; + + /// The records for each return type. + std::vector<Record *> RetTypeDefs; + + /// The MVT::SimpleValueType for each parameter type. Note that this list is + /// only populated when in the context of a target .td file. When building + /// Intrinsics.td, this isn't available, because we don't know the target + /// pointer size. + std::vector<MVT::SimpleValueType> ParamVTs; + + /// The records for each parameter type. + std::vector<Record *> ParamTypeDefs; }; - /// LoadIntrinsics - Read all of the intrinsics defined in the specified - /// .td file. - std::vector<CodeGenIntrinsic> LoadIntrinsics(const RecordKeeper &RC, - bool TargetOnly); + IntrinsicSignature IS; + + /// Bit flags describing the type (ref/mod) and location of memory + /// accesses that may be performed by the intrinsics. Analogous to + /// \c FunctionModRefBehaviour. + enum ModRefBits { + /// The intrinsic may access memory anywhere, i.e. it is not restricted + /// to access through pointer arguments. + MR_Anywhere = 1, + + /// The intrinsic may read memory. + MR_Ref = 2, + + /// The intrinsic may write memory. + MR_Mod = 4, + + /// The intrinsic may both read and write memory. + MR_ModRef = MR_Ref | MR_Mod, + }; + + /// Memory mod/ref behavior of this intrinsic, corresponding to intrinsic + /// properties (IntrReadMem, IntrArgMemOnly, etc.). + enum ModRefBehavior { + NoMem = 0, + ReadArgMem = MR_Ref, + ReadMem = MR_Ref | MR_Anywhere, + WriteArgMem = MR_Mod, + WriteMem = MR_Mod | MR_Anywhere, + ReadWriteArgMem = MR_ModRef, + ReadWriteMem = MR_ModRef | MR_Anywhere, + }; + ModRefBehavior ModRef; + + /// This is set to true if the intrinsic is overloaded by its argument + /// types. + bool isOverloaded; + + /// True if the intrinsic is commutative. + bool isCommutative; + + /// True if the intrinsic can throw. + bool canThrow; + + /// True if the intrinsic is marked as noduplicate. + bool isNoDuplicate; + + /// True if the intrinsic is no-return. + bool isNoReturn; + + /// True if the intrinsic is marked as convergent. + bool isConvergent; + + enum ArgAttribute { NoCapture, Returned, ReadOnly, WriteOnly, ReadNone }; + std::vector<std::pair<unsigned, ArgAttribute>> ArgumentAttributes; + + CodeGenIntrinsic(Record *R); +}; + +class CodeGenIntrinsicTable { + std::vector<CodeGenIntrinsic> Intrinsics; + +public: + struct TargetSet { + std::string Name; + size_t Offset; + size_t Count; + }; + std::vector<TargetSet> Targets; + + explicit CodeGenIntrinsicTable(const RecordKeeper &RC, bool TargetOnly); + CodeGenIntrinsicTable() = default; + + bool empty() const { return Intrinsics.empty(); } + size_t size() const { return Intrinsics.size(); } + CodeGenIntrinsic &operator[](size_t Pos) { return Intrinsics[Pos]; } + const CodeGenIntrinsic &operator[](size_t Pos) const { + return Intrinsics[Pos]; + } +}; } #endif diff --git a/utils/TableGen/CodeGenMapTable.cpp b/utils/TableGen/CodeGenMapTable.cpp index f66dd082709ba..527f530da479a 100644 --- a/utils/TableGen/CodeGenMapTable.cpp +++ b/utils/TableGen/CodeGenMapTable.cpp @@ -337,10 +337,20 @@ Record *MapTableEmitter::getInstrForColumn(Record *KeyInstr, } if (MatchFound) { - if (MatchInstr) // Already had a match + if (MatchInstr) { + // Already had a match // Error if multiple matches are found for a column. + std::string KeyValueStr; + for (Init *Value : KeyValue) { + if (!KeyValueStr.empty()) + KeyValueStr += ", "; + KeyValueStr += Value->getAsString(); + } + PrintFatalError("Multiple matches found for `" + KeyInstr->getName() + - "', for the relation `" + InstrMapDesc.getName()); + "', for the relation `" + InstrMapDesc.getName() + "', row fields [" + + KeyValueStr + "], column `" + CurValueCol->getAsString() + "'"); + } MatchInstr = CurInstr; } } @@ -355,7 +365,7 @@ Record *MapTableEmitter::getInstrForColumn(Record *KeyInstr, unsigned MapTableEmitter::emitBinSearchTable(raw_ostream &OS) { - const std::vector<const CodeGenInstruction*> &NumberedInstructions = + ArrayRef<const CodeGenInstruction*> NumberedInstructions = Target.getInstructionsByEnumValue(); std::string TargetName = Target.getName(); const std::vector<ListInit*> &ValueCols = InstrMapDesc.getValueCols(); @@ -499,8 +509,7 @@ static void emitEnums(raw_ostream &OS, RecordKeeper &Records) { // Iterate over all InstrMapping records and create a map between column // fields and their possible values across all records. - for (unsigned i = 0, e = InstrMapVec.size(); i < e; i++) { - Record *CurMap = InstrMapVec[i]; + for (Record *CurMap : InstrMapVec) { ListInit *ColFields; ColFields = CurMap->getValueAsListInit("ColFields"); ListInit *List = CurMap->getValueAsListInit("ValueCols"); @@ -524,10 +533,8 @@ static void emitEnums(raw_ostream &OS, RecordKeeper &Records) { } } - for (std::map<std::string, std::vector<Init*> >::iterator - II = ColFieldValueMap.begin(), IE = ColFieldValueMap.end(); - II != IE; II++) { - std::vector<Init*> FieldValues = (*II).second; + for (auto &Entry : ColFieldValueMap) { + std::vector<Init*> FieldValues = Entry.second; // Delete duplicate entries from ColFieldValueMap for (unsigned i = 0; i < FieldValues.size() - 1; i++) { @@ -540,9 +547,9 @@ static void emitEnums(raw_ostream &OS, RecordKeeper &Records) { } // Emit enumerated values for the column fields. - OS << "enum " << (*II).first << " {\n"; + OS << "enum " << Entry.first << " {\n"; for (unsigned i = 0, endFV = FieldValues.size(); i < endFV; i++) { - OS << "\t" << (*II).first << "_" << FieldValues[i]->getAsUnquotedString(); + OS << "\t" << Entry.first << "_" << FieldValues[i]->getAsUnquotedString(); if (i != endFV - 1) OS << ",\n"; else @@ -577,8 +584,8 @@ void EmitMapTable(RecordKeeper &Records, raw_ostream &OS) { // Iterate over all instruction mapping records and construct relationship // maps based on the information specified there. // - for (unsigned i = 0, e = InstrMapVec.size(); i < e; i++) { - MapTableEmitter IMap(Target, Records, InstrMapVec[i]); + for (Record *CurMap : InstrMapVec) { + MapTableEmitter IMap(Target, Records, CurMap); // Build RowInstrMap to group instructions based on their values for // RowFields. In the process, also collect key instructions into diff --git a/utils/TableGen/CodeGenRegisters.cpp b/utils/TableGen/CodeGenRegisters.cpp index ca316e96a21ad..626144fbe855a 100644 --- a/utils/TableGen/CodeGenRegisters.cpp +++ b/utils/TableGen/CodeGenRegisters.cpp @@ -587,10 +587,9 @@ struct TupleExpander : SetTheory::Expander { Elts.insert(NewReg); // Copy Proto super-classes. - ArrayRef<Record *> Supers = Proto->getSuperClasses(); - ArrayRef<SMRange> Ranges = Proto->getSuperClassRanges(); - for (unsigned i = 0, e = Supers.size(); i != e; ++i) - NewReg->addSuperClass(Supers[i], Ranges[i]); + ArrayRef<std::pair<Record *, SMRange>> Supers = Proto->getSuperClasses(); + for (const auto &SuperPair : Supers) + NewReg->addSuperClass(SuperPair.first, SuperPair.second); // Copy Proto fields. for (unsigned i = 0, e = Proto->getValues().size(); i != e; ++i) { @@ -1193,45 +1192,57 @@ void CodeGenRegBank::computeSubRegLaneMasks() { for (const auto &Idx : SubRegIndices) { const auto &Composites = Idx.getComposites(); auto &LaneTransforms = Idx.CompositionLaneMaskTransform; - // Go through all leaf subregisters and find the ones that compose with Idx. - // These make out all possible valid bits in the lane mask we want to - // transform. Looking only at the leafs ensure that only a single bit in - // the mask is set. - unsigned NextBit = 0; - for (auto &Idx2 : SubRegIndices) { - // Skip non-leaf subregisters. - if (!Idx2.getComposites().empty()) - continue; - // Replicate the behaviour from the lane mask generation loop above. - unsigned SrcBit = NextBit; - unsigned SrcMask = 1u << SrcBit; - if (NextBit < 31) - ++NextBit; - assert(Idx2.LaneMask == SrcMask); - - // Get the composed subregister if there is any. - auto C = Composites.find(&Idx2); - if (C == Composites.end()) - continue; - const CodeGenSubRegIndex *Composite = C->second; - // The Composed subreg should be a leaf subreg too - assert(Composite->getComposites().empty()); - - // Create Mask+Rotate operation and merge with existing ops if possible. - unsigned DstBit = Log2_32(Composite->LaneMask); - int Shift = DstBit - SrcBit; - uint8_t RotateLeft = Shift >= 0 ? (uint8_t)Shift : 32+Shift; - for (auto &I : LaneTransforms) { - if (I.RotateLeft == RotateLeft) { - I.Mask |= SrcMask; - SrcMask = 0; + + if (Composites.empty()) { + // Moving from a class with no subregisters we just had a single lane: + // The subregister must be a leaf subregister and only occupies 1 bit. + // Move the bit from the class without subregisters into that position. + unsigned DstBit = Log2_32(Idx.LaneMask); + assert(Idx.LaneMask == 1u << DstBit && "Must be a leaf subregister"); + MaskRolPair MaskRol = { 1, (uint8_t)DstBit }; + LaneTransforms.push_back(MaskRol); + } else { + // Go through all leaf subregisters and find the ones that compose with + // Idx. These make out all possible valid bits in the lane mask we want to + // transform. Looking only at the leafs ensure that only a single bit in + // the mask is set. + unsigned NextBit = 0; + for (auto &Idx2 : SubRegIndices) { + // Skip non-leaf subregisters. + if (!Idx2.getComposites().empty()) + continue; + // Replicate the behaviour from the lane mask generation loop above. + unsigned SrcBit = NextBit; + unsigned SrcMask = 1u << SrcBit; + if (NextBit < 31) + ++NextBit; + assert(Idx2.LaneMask == SrcMask); + + // Get the composed subregister if there is any. + auto C = Composites.find(&Idx2); + if (C == Composites.end()) + continue; + const CodeGenSubRegIndex *Composite = C->second; + // The Composed subreg should be a leaf subreg too + assert(Composite->getComposites().empty()); + + // Create Mask+Rotate operation and merge with existing ops if possible. + unsigned DstBit = Log2_32(Composite->LaneMask); + int Shift = DstBit - SrcBit; + uint8_t RotateLeft = Shift >= 0 ? (uint8_t)Shift : 32+Shift; + for (auto &I : LaneTransforms) { + if (I.RotateLeft == RotateLeft) { + I.Mask |= SrcMask; + SrcMask = 0; + } + } + if (SrcMask != 0) { + MaskRolPair MaskRol = { SrcMask, RotateLeft }; + LaneTransforms.push_back(MaskRol); } - } - if (SrcMask != 0) { - MaskRolPair MaskRol = { SrcMask, RotateLeft }; - LaneTransforms.push_back(MaskRol); } } + // Optimize if the transformation consists of one step only: Set mask to // 0xffffffff (including some irrelevant invalid bits) so that it should // merge with more entries later while compressing the table. @@ -1268,10 +1279,10 @@ void CodeGenRegBank::computeSubRegLaneMasks() { LaneMask |= SubRegIndex.LaneMask; } - // For classes without any subregisters set LaneMask to ~0u instead of 0. + // For classes without any subregisters set LaneMask to 1 instead of 0. // This makes it easier for client code to handle classes uniformly. if (LaneMask == 0) - LaneMask = ~0u; + LaneMask = 1; RegClass.LaneMask = LaneMask; } @@ -1818,11 +1829,14 @@ void CodeGenRegBank::computeDerivedInfo() { computeRegUnitLaneMasks(); - // Compute register class HasDisjunctSubRegs flag. + // Compute register class HasDisjunctSubRegs/CoveredBySubRegs flag. for (CodeGenRegisterClass &RC : RegClasses) { RC.HasDisjunctSubRegs = false; - for (const CodeGenRegister *Reg : RC.getMembers()) + RC.CoveredBySubRegs = true; + for (const CodeGenRegister *Reg : RC.getMembers()) { RC.HasDisjunctSubRegs |= Reg->HasDisjunctSubRegs; + RC.CoveredBySubRegs &= Reg->CoveredBySubRegs; + } } // Get the weight of each set. diff --git a/utils/TableGen/CodeGenRegisters.h b/utils/TableGen/CodeGenRegisters.h index dc441436537db..b8d47aa4ff82d 100644 --- a/utils/TableGen/CodeGenRegisters.h +++ b/utils/TableGen/CodeGenRegisters.h @@ -19,22 +19,21 @@ #include "llvm/ADT/BitVector.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/SetVector.h" #include "llvm/ADT/SparseBitVector.h" #include "llvm/CodeGen/MachineValueType.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/TableGen/Record.h" #include "llvm/TableGen/SetTheory.h" #include <cstdlib> +#include <deque> #include <list> #include <map> -#include <set> #include <string> #include <vector> -#include <deque> namespace llvm { class CodeGenRegBank; + template <typename T, typename Vector, typename Set> class SetVector; /// Used to encode a step in a register lane mask transformation. /// Mask the bits specified in Mask, then rotate them Rol bits to the left @@ -311,6 +310,7 @@ namespace llvm { unsigned LaneMask; /// True if there are at least 2 subregisters which do not interfere. bool HasDisjunctSubRegs; + bool CoveredBySubRegs; // Return the Record that defined this class, or NULL if the class was // created by TableGen. diff --git a/utils/TableGen/CodeGenSchedule.cpp b/utils/TableGen/CodeGenSchedule.cpp index c98f62345342d..d1b141e3160f5 100644 --- a/utils/TableGen/CodeGenSchedule.cpp +++ b/utils/TableGen/CodeGenSchedule.cpp @@ -68,7 +68,7 @@ struct InstRegexOp : public SetTheory::Operator { } RegexList.push_back(Regex(pat)); } - for (const CodeGenInstruction *Inst : Target.instructions()) { + for (const CodeGenInstruction *Inst : Target.getInstructionsByEnumValue()) { for (auto &R : RegexList) { if (R.match(Inst->TheDef->getName())) Elts.insert(Inst->TheDef); @@ -120,12 +120,18 @@ CodeGenSchedModels::CodeGenSchedModels(RecordKeeper &RK, // (For per-operand resources mapped to itinerary classes). collectProcItinRW(); + // Find UnsupportedFeatures records for each processor. + // (For per-operand resources mapped to itinerary classes). + collectProcUnsupportedFeatures(); + // Infer new SchedClasses from SchedVariant. inferSchedClasses(); // Populate each CodeGenProcModel's WriteResDefs, ReadAdvanceDefs, and // ProcResourceDefs. collectProcResources(); + + checkCompleteness(); } /// Gather all processor models. @@ -204,7 +210,7 @@ void CodeGenSchedModels::collectSchedRW() { // Find all SchedReadWrites referenced by instruction defs. RecVec SWDefs, SRDefs; - for (const CodeGenInstruction *Inst : Target.instructions()) { + for (const CodeGenInstruction *Inst : Target.getInstructionsByEnumValue()) { Record *SchedDef = Inst->TheDef; if (SchedDef->isValueUnset("SchedRW")) continue; @@ -498,7 +504,7 @@ void CodeGenSchedModels::collectSchedClasses() { // Create a SchedClass for each unique combination of itinerary class and // SchedRW list. - for (const CodeGenInstruction *Inst : Target.instructions()) { + for (const CodeGenInstruction *Inst : Target.getInstructionsByEnumValue()) { Record *ItinDef = Inst->TheDef->getValueAsDef("Itinerary"); IdxVec Writes, Reads; if (!Inst->TheDef->isValueUnset("SchedRW")) @@ -523,11 +529,12 @@ void CodeGenSchedModels::collectSchedClasses() { if (!EnableDump) return; - for (const CodeGenInstruction *Inst : Target.instructions()) { + for (const CodeGenInstruction *Inst : Target.getInstructionsByEnumValue()) { std::string InstName = Inst->TheDef->getName(); unsigned SCIdx = InstrClassMap.lookup(Inst->TheDef); if (!SCIdx) { - dbgs() << "No machine model for " << Inst->TheDef->getName() << '\n'; + if (!Inst->hasNoSchedulingInfo) + dbgs() << "No machine model for " << Inst->TheDef->getName() << '\n'; continue; } CodeGenSchedClass &SC = getSchedClass(SCIdx); @@ -826,6 +833,15 @@ void CodeGenSchedModels::collectProcItinRW() { } } +// Gather the unsupported features for processor models. +void CodeGenSchedModels::collectProcUnsupportedFeatures() { + for (CodeGenProcModel &ProcModel : ProcModels) { + for (Record *Pred : ProcModel.ModelDef->getValueAsListOfDefs("UnsupportedFeatures")) { + ProcModel.UnsupportedFeaturesDefs.push_back(Pred); + } + } +} + /// Infer new classes from existing classes. In the process, this may create new /// SchedWrites from sequences of existing SchedWrites. void CodeGenSchedModels::inferSchedClasses() { @@ -1426,6 +1442,9 @@ void CodeGenSchedModels::verifyProcResourceGroups(CodeGenProcModel &PM) { // Collect and sort WriteRes, ReadAdvance, and ProcResources. void CodeGenSchedModels::collectProcResources() { + ProcResourceDefs = Records.getAllDerivedDefinitions("ProcResourceUnits"); + ProcResGroups = Records.getAllDerivedDefinitions("ProcResGroup"); + // Add any subtarget-specific SchedReadWrites that are directly associated // with processor resources. Refer to the parent SchedClass's ProcIndices to // determine which processors they apply to. @@ -1520,6 +1539,63 @@ void CodeGenSchedModels::collectProcResources() { dbgs() << '\n'); verifyProcResourceGroups(PM); } + + ProcResourceDefs.clear(); + ProcResGroups.clear(); +} + +void CodeGenSchedModels::checkCompleteness() { + bool Complete = true; + bool HadCompleteModel = false; + for (const CodeGenProcModel &ProcModel : procModels()) { + if (!ProcModel.ModelDef->getValueAsBit("CompleteModel")) + continue; + for (const CodeGenInstruction *Inst : Target.getInstructionsByEnumValue()) { + if (Inst->hasNoSchedulingInfo) + continue; + if (ProcModel.isUnsupported(*Inst)) + continue; + unsigned SCIdx = getSchedClassIdx(*Inst); + if (!SCIdx) { + if (Inst->TheDef->isValueUnset("SchedRW") && !HadCompleteModel) { + PrintError("No schedule information for instruction '" + + Inst->TheDef->getName() + "'"); + Complete = false; + } + continue; + } + + const CodeGenSchedClass &SC = getSchedClass(SCIdx); + if (!SC.Writes.empty()) + continue; + if (SC.ItinClassDef != nullptr) + continue; + + const RecVec &InstRWs = SC.InstRWs; + auto I = std::find_if(InstRWs.begin(), InstRWs.end(), + [&ProcModel] (const Record *R) { + return R->getValueAsDef("SchedModel") == + ProcModel.ModelDef; + }); + if (I == InstRWs.end()) { + PrintError("'" + ProcModel.ModelName + "' lacks information for '" + + Inst->TheDef->getName() + "'"); + Complete = false; + } + } + HadCompleteModel = true; + } + if (!Complete) { + errs() << "\n\nIncomplete schedule models found.\n" + << "- Consider setting 'CompleteModel = 0' while developing new models.\n" + << "- Pseudo instructions can be marked with 'hasNoSchedulingInfo = 1'.\n" + << "- Instructions should usually have Sched<[...]> as a superclass, " + "you may temporarily use an empty list.\n" + << "- Instructions related to unsupported features can be excluded with " + "list<Predicate> UnsupportedFeatures = [HasA,..,HasY]; in the " + "processor model.\n\n"; + PrintFatalError("Incomplete schedule model"); + } } // Collect itinerary class resources for each processor. @@ -1600,8 +1676,8 @@ Record *CodeGenSchedModels::findProcResUnits(Record *ProcResKind, return ProcResKind; Record *ProcUnitDef = nullptr; - RecVec ProcResourceDefs = - Records.getAllDerivedDefinitions("ProcResourceUnits"); + assert(!ProcResourceDefs.empty()); + assert(!ProcResGroups.empty()); for (RecIter RI = ProcResourceDefs.begin(), RE = ProcResourceDefs.end(); RI != RE; ++RI) { @@ -1616,7 +1692,6 @@ Record *CodeGenSchedModels::findProcResUnits(Record *ProcResKind, ProcUnitDef = *RI; } } - RecVec ProcResGroups = Records.getAllDerivedDefinitions("ProcResGroup"); for (RecIter RI = ProcResGroups.begin(), RE = ProcResGroups.end(); RI != RE; ++RI) { @@ -1699,6 +1774,16 @@ unsigned CodeGenProcModel::getProcResourceIdx(Record *PRDef) const { return 1 + (PRPos - ProcResourceDefs.begin()); } +bool CodeGenProcModel::isUnsupported(const CodeGenInstruction &Inst) const { + for (const Record *TheDef : UnsupportedFeaturesDefs) { + for (const Record *PredDef : Inst.TheDef->getValueAsListOfDefs("Predicates")) { + if (TheDef->getName() == PredDef->getName()) + return true; + } + } + return false; +} + #ifndef NDEBUG void CodeGenProcModel::dump() const { dbgs() << Index << ": " << ModelName << " " diff --git a/utils/TableGen/CodeGenSchedule.h b/utils/TableGen/CodeGenSchedule.h index f5c50c992a928..755ffd25b0cbc 100644 --- a/utils/TableGen/CodeGenSchedule.h +++ b/utils/TableGen/CodeGenSchedule.h @@ -189,6 +189,10 @@ struct CodeGenProcModel { // This list is empty if no ItinRW refers to this Processor. RecVec ItinRWDefs; + // List of unsupported feature. + // This list is empty if the Processor has no UnsupportedFeatures. + RecVec UnsupportedFeaturesDefs; + // All read/write resources associated with this processor. RecVec WriteResDefs; RecVec ReadAdvanceDefs; @@ -211,6 +215,8 @@ struct CodeGenProcModel { unsigned getProcResourceIdx(Record *PRDef) const; + bool isUnsupported(const CodeGenInstruction &Inst) const; + #ifndef NDEBUG void dump() const; #endif @@ -241,6 +247,9 @@ class CodeGenSchedModels { // Any inferred SchedClass has an index greater than NumInstrSchedClassses. unsigned NumInstrSchedClasses; + RecVec ProcResourceDefs; + RecVec ProcResGroups; + // Map each instruction to its unique SchedClass index considering the // combination of it's itinerary class, SchedRW list, and InstRW records. typedef DenseMap<Record*, unsigned> InstClassMapTy; @@ -300,6 +309,7 @@ public: typedef std::vector<CodeGenProcModel>::const_iterator ProcIter; ProcIter procModelBegin() const { return ProcModels.begin(); } ProcIter procModelEnd() const { return ProcModels.end(); } + ArrayRef<CodeGenProcModel> procModels() const { return ProcModels; } // Return true if any processors have itineraries. bool hasItineraries() const; @@ -353,6 +363,7 @@ public: typedef std::vector<CodeGenSchedClass>::const_iterator SchedClassIter; SchedClassIter schedClassBegin() const { return SchedClasses.begin(); } SchedClassIter schedClassEnd() const { return SchedClasses.end(); } + ArrayRef<CodeGenSchedClass> schedClasses() const { return SchedClasses; } unsigned numInstrSchedClasses() const { return NumInstrSchedClasses; } @@ -397,8 +408,12 @@ private: void collectProcItinRW(); + void collectProcUnsupportedFeatures(); + void inferSchedClasses(); + void checkCompleteness(); + void inferFromRW(ArrayRef<unsigned> OperWrites, ArrayRef<unsigned> OperReads, unsigned FromClassIdx, ArrayRef<unsigned> ProcIndices); void inferFromItinClass(Record *ItinClassDef, unsigned FromClassIdx); diff --git a/utils/TableGen/CodeGenTarget.cpp b/utils/TableGen/CodeGenTarget.cpp index aaad4225ace88..245b9eeeed858 100644 --- a/utils/TableGen/CodeGenTarget.cpp +++ b/utils/TableGen/CodeGenTarget.cpp @@ -39,7 +39,7 @@ MVT::SimpleValueType llvm::getValueType(Record *Rec) { return (MVT::SimpleValueType)Rec->getValueAsInt("Value"); } -std::string llvm::getName(MVT::SimpleValueType T) { +StringRef llvm::getName(MVT::SimpleValueType T) { switch (T) { case MVT::Other: return "UNKNOWN"; case MVT::iPTR: return "TLI.getPointerTy()"; @@ -48,7 +48,7 @@ std::string llvm::getName(MVT::SimpleValueType T) { } } -std::string llvm::getEnumName(MVT::SimpleValueType T) { +StringRef llvm::getEnumName(MVT::SimpleValueType T) { switch (T) { case MVT::Other: return "MVT::Other"; case MVT::i1: return "MVT::i1"; @@ -162,7 +162,7 @@ const std::string &CodeGenTarget::getName() const { } std::string CodeGenTarget::getInstNamespace() const { - for (const CodeGenInstruction *Inst : instructions()) { + for (const CodeGenInstruction *Inst : getInstructionsByEnumValue()) { // Make sure not to pick up "TargetOpcode" by accidentally getting // the namespace off the PHI instruction or something. if (Inst->Namespace != "TargetOpcode") @@ -300,14 +300,9 @@ GetInstByName(const char *Name, /// \brief Return all of the instructions defined by the target, ordered by /// their enum value. void CodeGenTarget::ComputeInstrsByEnum() const { - // The ordering here must match the ordering in TargetOpcodes.h. static const char *const FixedInstrs[] = { - "PHI", "INLINEASM", "CFI_INSTRUCTION", "EH_LABEL", - "GC_LABEL", "KILL", "EXTRACT_SUBREG", "INSERT_SUBREG", - "IMPLICIT_DEF", "SUBREG_TO_REG", "COPY_TO_REGCLASS", "DBG_VALUE", - "REG_SEQUENCE", "COPY", "BUNDLE", "LIFETIME_START", - "LIFETIME_END", "STACKMAP", "PATCHPOINT", "LOAD_STACK_GUARD", - "STATEPOINT", "LOCAL_ESCAPE", "FAULTING_LOAD_OP", +#define HANDLE_TARGET_OPCODE(OPC, NUM) #OPC, +#include "llvm/Target/TargetOpcodes.def" nullptr}; const auto &Insts = getInstructions(); for (const char *const *p = FixedInstrs; *p; ++p) { @@ -357,9 +352,9 @@ void CodeGenTarget::reverseBitsForLittleEndianEncoding() { BitsInit *BI = R->getValueAsBitsInit("Inst"); unsigned numBits = BI->getNumBits(); - + SmallVector<Init *, 16> NewBits(numBits); - + for (unsigned bit = 0, end = numBits / 2; bit != end; ++bit) { unsigned bitSwapIdx = numBits - bit - 1; Init *OrigBit = BI->getBit(bit); @@ -431,18 +426,29 @@ ComplexPattern::ComplexPattern(Record *R) { // CodeGenIntrinsic Implementation //===----------------------------------------------------------------------===// -std::vector<CodeGenIntrinsic> llvm::LoadIntrinsics(const RecordKeeper &RC, - bool TargetOnly) { - std::vector<Record*> I = RC.getAllDerivedDefinitions("Intrinsic"); +CodeGenIntrinsicTable::CodeGenIntrinsicTable(const RecordKeeper &RC, + bool TargetOnly) { + std::vector<Record*> Defs = RC.getAllDerivedDefinitions("Intrinsic"); - std::vector<CodeGenIntrinsic> Result; + Intrinsics.reserve(Defs.size()); - for (unsigned i = 0, e = I.size(); i != e; ++i) { - bool isTarget = I[i]->getValueAsBit("isTarget"); + for (unsigned I = 0, e = Defs.size(); I != e; ++I) { + bool isTarget = Defs[I]->getValueAsBit("isTarget"); if (isTarget == TargetOnly) - Result.push_back(CodeGenIntrinsic(I[i])); + Intrinsics.push_back(CodeGenIntrinsic(Defs[I])); } - return Result; + std::sort(Intrinsics.begin(), Intrinsics.end(), + [](const CodeGenIntrinsic &LHS, const CodeGenIntrinsic &RHS) { + return std::tie(LHS.TargetPrefix, LHS.Name) < + std::tie(RHS.TargetPrefix, RHS.Name); + }); + Targets.push_back({"", 0, 0}); + for (size_t I = 0, E = Intrinsics.size(); I < E; ++I) + if (Intrinsics[I].TargetPrefix != Targets.back().Name) { + Targets.back().Count = I - Targets.back().Offset; + Targets.push_back({Intrinsics[I].TargetPrefix, I, 0}); + } + Targets.back().Count = Intrinsics.size() - Targets.back().Offset; } CodeGenIntrinsic::CodeGenIntrinsic(Record *R) { @@ -565,7 +571,7 @@ CodeGenIntrinsic::CodeGenIntrinsic(Record *R) { } // Parse the intrinsic properties. - ListInit *PropList = R->getValueAsListInit("Properties"); + ListInit *PropList = R->getValueAsListInit("IntrProperties"); for (unsigned i = 0, e = PropList->size(); i != e; ++i) { Record *Property = PropList->getElementAsRecord(i); assert(Property->isSubClassOf("IntrinsicProperty") && @@ -573,12 +579,12 @@ CodeGenIntrinsic::CodeGenIntrinsic(Record *R) { if (Property->getName() == "IntrNoMem") ModRef = NoMem; - else if (Property->getName() == "IntrReadArgMem") - ModRef = ReadArgMem; else if (Property->getName() == "IntrReadMem") - ModRef = ReadMem; - else if (Property->getName() == "IntrReadWriteArgMem") - ModRef = ReadWriteArgMem; + ModRef = ModRefBehavior(ModRef & ~MR_Mod); + else if (Property->getName() == "IntrWriteMem") + ModRef = ModRefBehavior(ModRef & ~MR_Ref); + else if (Property->getName() == "IntrArgMemOnly") + ModRef = ModRefBehavior(ModRef & ~MR_Anywhere); else if (Property->getName() == "Commutative") isCommutative = true; else if (Property->getName() == "Throws") @@ -592,9 +598,15 @@ CodeGenIntrinsic::CodeGenIntrinsic(Record *R) { else if (Property->isSubClassOf("NoCapture")) { unsigned ArgNo = Property->getValueAsInt("ArgNo"); ArgumentAttributes.push_back(std::make_pair(ArgNo, NoCapture)); + } else if (Property->isSubClassOf("Returned")) { + unsigned ArgNo = Property->getValueAsInt("ArgNo"); + ArgumentAttributes.push_back(std::make_pair(ArgNo, Returned)); } else if (Property->isSubClassOf("ReadOnly")) { unsigned ArgNo = Property->getValueAsInt("ArgNo"); ArgumentAttributes.push_back(std::make_pair(ArgNo, ReadOnly)); + } else if (Property->isSubClassOf("WriteOnly")) { + unsigned ArgNo = Property->getValueAsInt("ArgNo"); + ArgumentAttributes.push_back(std::make_pair(ArgNo, WriteOnly)); } else if (Property->isSubClassOf("ReadNone")) { unsigned ArgNo = Property->getValueAsInt("ArgNo"); ArgumentAttributes.push_back(std::make_pair(ArgNo, ReadNone)); diff --git a/utils/TableGen/CodeGenTarget.h b/utils/TableGen/CodeGenTarget.h index cf4a0bbe5bd9e..85a8c1b188785 100644 --- a/utils/TableGen/CodeGenTarget.h +++ b/utils/TableGen/CodeGenTarget.h @@ -52,8 +52,8 @@ enum SDNP { /// record corresponds to. MVT::SimpleValueType getValueType(Record *Rec); -std::string getName(MVT::SimpleValueType T); -std::string getEnumName(MVT::SimpleValueType T); +StringRef getName(MVT::SimpleValueType T); +StringRef getEnumName(MVT::SimpleValueType T); /// getQualifiedName - Return the name of the specified record, with a /// namespace qualifier if the record contains one. @@ -139,9 +139,7 @@ public: /// supported by the target (i.e. there are registers that directly hold it). bool isLegalValueType(MVT::SimpleValueType VT) const { ArrayRef<MVT::SimpleValueType> LegalVTs = getLegalValueTypes(); - for (unsigned i = 0, e = LegalVTs.size(); i != e; ++i) - if (LegalVTs[i] == VT) return true; - return false; + return std::find(LegalVTs.begin(), LegalVTs.end(), VT) != LegalVTs.end(); } CodeGenSchedModels &getSchedModels() const; @@ -163,18 +161,15 @@ public: /// getInstructionsByEnumValue - Return all of the instructions defined by the /// target, ordered by their enum value. - const std::vector<const CodeGenInstruction*> & + ArrayRef<const CodeGenInstruction *> getInstructionsByEnumValue() const { if (InstrsByEnum.empty()) ComputeInstrsByEnum(); return InstrsByEnum; } - typedef std::vector<const CodeGenInstruction*>::const_iterator inst_iterator; + typedef ArrayRef<const CodeGenInstruction *>::const_iterator inst_iterator; inst_iterator inst_begin() const{return getInstructionsByEnumValue().begin();} inst_iterator inst_end() const { return getInstructionsByEnumValue().end(); } - iterator_range<inst_iterator> instructions() const { - return make_range(inst_begin(), inst_end()); - } /// isLittleEndianEncoding - are instruction bit patterns defined as [0..n]? diff --git a/utils/TableGen/DAGISelMatcher.cpp b/utils/TableGen/DAGISelMatcher.cpp index 9c4079906a383..6ac3958e0f430 100644 --- a/utils/TableGen/DAGISelMatcher.cpp +++ b/utils/TableGen/DAGISelMatcher.cpp @@ -225,12 +225,14 @@ void CheckFoldableChainNodeMatcher::printImpl(raw_ostream &OS, } void EmitIntegerMatcher::printImpl(raw_ostream &OS, unsigned indent) const { - OS.indent(indent) << "EmitInteger " << Val << " VT=" << VT << '\n'; + OS.indent(indent) << "EmitInteger " << Val << " VT=" << getEnumName(VT) + << '\n'; } void EmitStringIntegerMatcher:: printImpl(raw_ostream &OS, unsigned indent) const { - OS.indent(indent) << "EmitStringInteger " << Val << " VT=" << VT << '\n'; + OS.indent(indent) << "EmitStringInteger " << Val << " VT=" << getEnumName(VT) + << '\n'; } void EmitRegisterMatcher::printImpl(raw_ostream &OS, unsigned indent) const { @@ -239,7 +241,7 @@ void EmitRegisterMatcher::printImpl(raw_ostream &OS, unsigned indent) const { OS << Reg->getName(); else OS << "zero_reg"; - OS << " VT=" << VT << '\n'; + OS << " VT=" << getEnumName(VT) << '\n'; } void EmitConvertToTargetMatcher:: @@ -275,54 +277,12 @@ void EmitNodeMatcherCommon::printImpl(raw_ostream &OS, unsigned indent) const { OS << ")\n"; } -void MarkGlueResultsMatcher::printImpl(raw_ostream &OS, unsigned indent) const { - OS.indent(indent) << "MarkGlueResults <todo: args>\n"; -} - void CompleteMatchMatcher::printImpl(raw_ostream &OS, unsigned indent) const { OS.indent(indent) << "CompleteMatch <todo args>\n"; OS.indent(indent) << "Src = " << *Pattern.getSrcPattern() << "\n"; OS.indent(indent) << "Dst = " << *Pattern.getDstPattern() << "\n"; } -// getHashImpl Implementation. - -unsigned CheckPatternPredicateMatcher::getHashImpl() const { - return HashString(Predicate); -} - -unsigned CheckPredicateMatcher::getHashImpl() const { - return HashString(getPredicate().getFnName()); -} - -unsigned CheckOpcodeMatcher::getHashImpl() const { - return HashString(Opcode.getEnumName()); -} - -unsigned CheckCondCodeMatcher::getHashImpl() const { - return HashString(CondCodeName); -} - -unsigned CheckValueTypeMatcher::getHashImpl() const { - return HashString(TypeName); -} - -unsigned EmitStringIntegerMatcher::getHashImpl() const { - return HashString(Val) ^ VT; -} - -template<typename It> -static unsigned HashUnsigneds(It I, It E) { - unsigned Result = 0; - for (; I != E; ++I) - Result = (Result<<3) ^ *I; - return Result; -} - -unsigned EmitMergeInputChainsMatcher::getHashImpl() const { - return HashUnsigneds(ChainNodes.begin(), ChainNodes.end()); -} - bool CheckOpcodeMatcher::isEqualImpl(const Matcher *M) const { // Note: pointer equality isn't enough here, we have to check the enum names // to ensure that the nodes are for the same opcode. @@ -339,24 +299,10 @@ bool EmitNodeMatcherCommon::isEqualImpl(const Matcher *m) const { M->NumFixedArityOperands == NumFixedArityOperands; } -unsigned EmitNodeMatcherCommon::getHashImpl() const { - return (HashString(OpcodeName) << 4) | Operands.size(); -} - - void EmitNodeMatcher::anchor() { } void MorphNodeToMatcher::anchor() { } -unsigned MarkGlueResultsMatcher::getHashImpl() const { - return HashUnsigneds(GlueResultNodes.begin(), GlueResultNodes.end()); -} - -unsigned CompleteMatchMatcher::getHashImpl() const { - return HashUnsigneds(Results.begin(), Results.end()) ^ - ((unsigned)(intptr_t)&Pattern << 8); -} - // isContradictoryImpl Implementations. static bool TypesAreContradictory(MVT::SimpleValueType T1, diff --git a/utils/TableGen/DAGISelMatcher.h b/utils/TableGen/DAGISelMatcher.h index a8a6ba5c32e15..6bda9ca5f96fa 100644 --- a/utils/TableGen/DAGISelMatcher.h +++ b/utils/TableGen/DAGISelMatcher.h @@ -82,7 +82,6 @@ public: EmitCopyToReg, // Emit a copytoreg into a physreg. EmitNode, // Create a DAG node EmitNodeXForm, // Run a SDNodeXForm - MarkGlueResults, // Indicate which interior nodes have glue results. CompleteMatch, // Finish a match and update the results. MorphNodeTo // Build a node, finish a match and update results. }; @@ -107,17 +106,6 @@ public: return isEqualImpl(M); } - unsigned getHash() const { - // Clear the high bit so we don't conflict with tombstones etc. - return ((getHashImpl() << 4) ^ getKind()) & (~0U>>1); - } - - /// isSafeToReorderWithPatternPredicate - Return true if it is safe to sink a - /// PatternPredicate node past this one. - virtual bool isSafeToReorderWithPatternPredicate() const { - return false; - } - /// isSimplePredicateNode - Return true if this is a simple predicate that /// operates on the node or its children without potential side effects or a /// change of the current node. @@ -181,7 +169,6 @@ public: protected: virtual void printImpl(raw_ostream &OS, unsigned indent) const = 0; virtual bool isEqualImpl(const Matcher *M) const = 0; - virtual unsigned getHashImpl() const = 0; virtual bool isContradictoryImpl(const Matcher *M) const { return false; } }; @@ -228,7 +215,6 @@ public: private: void printImpl(raw_ostream &OS, unsigned indent) const override; bool isEqualImpl(const Matcher *M) const override { return false; } - unsigned getHashImpl() const override { return 12312; } }; /// RecordMatcher - Save the current node in the operand list. @@ -251,11 +237,9 @@ public: return N->getKind() == RecordNode; } - bool isSafeToReorderWithPatternPredicate() const override { return true; } private: void printImpl(raw_ostream &OS, unsigned indent) const override; bool isEqualImpl(const Matcher *M) const override { return true; } - unsigned getHashImpl() const override { return 0; } }; /// RecordChildMatcher - Save a numbered child of the current node, or fail @@ -285,14 +269,11 @@ public: return N->getKind() == RecordChild; } - bool isSafeToReorderWithPatternPredicate() const override { return true; } - private: void printImpl(raw_ostream &OS, unsigned indent) const override; bool isEqualImpl(const Matcher *M) const override { return cast<RecordChildMatcher>(M)->getChildNo() == getChildNo(); } - unsigned getHashImpl() const override { return getChildNo(); } }; /// RecordMemRefMatcher - Save the current node's memref. @@ -304,12 +285,9 @@ public: return N->getKind() == RecordMemRef; } - bool isSafeToReorderWithPatternPredicate() const override { return true; } - private: void printImpl(raw_ostream &OS, unsigned indent) const override; bool isEqualImpl(const Matcher *M) const override { return true; } - unsigned getHashImpl() const override { return 0; } }; @@ -323,12 +301,9 @@ public: return N->getKind() == CaptureGlueInput; } - bool isSafeToReorderWithPatternPredicate() const override { return true; } - private: void printImpl(raw_ostream &OS, unsigned indent) const override; bool isEqualImpl(const Matcher *M) const override { return true; } - unsigned getHashImpl() const override { return 0; } }; /// MoveChildMatcher - This tells the interpreter to move into the @@ -344,14 +319,11 @@ public: return N->getKind() == MoveChild; } - bool isSafeToReorderWithPatternPredicate() const override { return true; } - private: void printImpl(raw_ostream &OS, unsigned indent) const override; bool isEqualImpl(const Matcher *M) const override { return cast<MoveChildMatcher>(M)->getChildNo() == getChildNo(); } - unsigned getHashImpl() const override { return getChildNo(); } }; /// MoveParentMatcher - This tells the interpreter to move to the parent @@ -364,12 +336,9 @@ public: return N->getKind() == MoveParent; } - bool isSafeToReorderWithPatternPredicate() const override { return true; } - private: void printImpl(raw_ostream &OS, unsigned indent) const override; bool isEqualImpl(const Matcher *M) const override { return true; } - unsigned getHashImpl() const override { return 0; } }; /// CheckSameMatcher - This checks to see if this node is exactly the same @@ -387,14 +356,11 @@ public: return N->getKind() == CheckSame; } - bool isSafeToReorderWithPatternPredicate() const override { return true; } - private: void printImpl(raw_ostream &OS, unsigned indent) const override; bool isEqualImpl(const Matcher *M) const override { return cast<CheckSameMatcher>(M)->getMatchNumber() == getMatchNumber(); } - unsigned getHashImpl() const override { return getMatchNumber(); } }; /// CheckChildSameMatcher - This checks to see if child node is exactly the same @@ -414,15 +380,12 @@ public: return N->getKind() == CheckChildSame; } - bool isSafeToReorderWithPatternPredicate() const override { return true; } - private: void printImpl(raw_ostream &OS, unsigned indent) const override; bool isEqualImpl(const Matcher *M) const override { return cast<CheckChildSameMatcher>(M)->ChildNo == ChildNo && cast<CheckChildSameMatcher>(M)->MatchNumber == MatchNumber; } - unsigned getHashImpl() const override { return (MatchNumber << 2) | ChildNo; } }; /// CheckPatternPredicateMatcher - This checks the target-specific predicate @@ -440,14 +403,11 @@ public: return N->getKind() == CheckPatternPredicate; } - bool isSafeToReorderWithPatternPredicate() const override { return true; } - private: void printImpl(raw_ostream &OS, unsigned indent) const override; bool isEqualImpl(const Matcher *M) const override { return cast<CheckPatternPredicateMatcher>(M)->getPredicate() == Predicate; } - unsigned getHashImpl() const override; }; /// CheckPredicateMatcher - This checks the target-specific predicate to @@ -463,15 +423,11 @@ public: return N->getKind() == CheckPredicate; } - // TODO: Ok? - //virtual bool isSafeToReorderWithPatternPredicate() const { return true; } - private: void printImpl(raw_ostream &OS, unsigned indent) const override; bool isEqualImpl(const Matcher *M) const override { return cast<CheckPredicateMatcher>(M)->Pred == Pred; } - unsigned getHashImpl() const override; }; @@ -489,12 +445,9 @@ public: return N->getKind() == CheckOpcode; } - bool isSafeToReorderWithPatternPredicate() const override { return true; } - private: void printImpl(raw_ostream &OS, unsigned indent) const override; bool isEqualImpl(const Matcher *M) const override; - unsigned getHashImpl() const override; bool isContradictoryImpl(const Matcher *M) const override; }; @@ -522,7 +475,6 @@ public: private: void printImpl(raw_ostream &OS, unsigned indent) const override; bool isEqualImpl(const Matcher *M) const override { return false; } - unsigned getHashImpl() const override { return 4123; } }; /// CheckTypeMatcher - This checks to see if the current node has the @@ -541,14 +493,11 @@ public: return N->getKind() == CheckType; } - bool isSafeToReorderWithPatternPredicate() const override { return true; } - private: void printImpl(raw_ostream &OS, unsigned indent) const override; bool isEqualImpl(const Matcher *M) const override { return cast<CheckTypeMatcher>(M)->Type == Type; } - unsigned getHashImpl() const override { return Type; } bool isContradictoryImpl(const Matcher *M) const override; }; @@ -576,7 +525,6 @@ public: private: void printImpl(raw_ostream &OS, unsigned indent) const override; bool isEqualImpl(const Matcher *M) const override { return false; } - unsigned getHashImpl() const override { return 4123; } }; @@ -596,15 +544,12 @@ public: return N->getKind() == CheckChildType; } - bool isSafeToReorderWithPatternPredicate() const override { return true; } - private: void printImpl(raw_ostream &OS, unsigned indent) const override; bool isEqualImpl(const Matcher *M) const override { return cast<CheckChildTypeMatcher>(M)->ChildNo == ChildNo && cast<CheckChildTypeMatcher>(M)->Type == Type; } - unsigned getHashImpl() const override { return (Type << 3) | ChildNo; } bool isContradictoryImpl(const Matcher *M) const override; }; @@ -623,14 +568,11 @@ public: return N->getKind() == CheckInteger; } - bool isSafeToReorderWithPatternPredicate() const override { return true; } - private: void printImpl(raw_ostream &OS, unsigned indent) const override; bool isEqualImpl(const Matcher *M) const override { return cast<CheckIntegerMatcher>(M)->Value == Value; } - unsigned getHashImpl() const override { return Value; } bool isContradictoryImpl(const Matcher *M) const override; }; @@ -650,15 +592,12 @@ public: return N->getKind() == CheckChildInteger; } - bool isSafeToReorderWithPatternPredicate() const override { return true; } - private: void printImpl(raw_ostream &OS, unsigned indent) const override; bool isEqualImpl(const Matcher *M) const override { return cast<CheckChildIntegerMatcher>(M)->ChildNo == ChildNo && cast<CheckChildIntegerMatcher>(M)->Value == Value; } - unsigned getHashImpl() const override { return (Value << 3) | ChildNo; } bool isContradictoryImpl(const Matcher *M) const override; }; @@ -676,14 +615,11 @@ public: return N->getKind() == CheckCondCode; } - bool isSafeToReorderWithPatternPredicate() const override { return true; } - private: void printImpl(raw_ostream &OS, unsigned indent) const override; bool isEqualImpl(const Matcher *M) const override { return cast<CheckCondCodeMatcher>(M)->CondCodeName == CondCodeName; } - unsigned getHashImpl() const override; }; /// CheckValueTypeMatcher - This checks to see if the current node is a @@ -700,14 +636,11 @@ public: return N->getKind() == CheckValueType; } - bool isSafeToReorderWithPatternPredicate() const override { return true; } - private: void printImpl(raw_ostream &OS, unsigned indent) const override; bool isEqualImpl(const Matcher *M) const override { return cast<CheckValueTypeMatcher>(M)->TypeName == TypeName; } - unsigned getHashImpl() const override; bool isContradictoryImpl(const Matcher *M) const override; }; @@ -744,18 +677,12 @@ public: return N->getKind() == CheckComplexPat; } - // Not safe to move a pattern predicate past a complex pattern. - bool isSafeToReorderWithPatternPredicate() const override { return false; } - private: void printImpl(raw_ostream &OS, unsigned indent) const override; bool isEqualImpl(const Matcher *M) const override { return &cast<CheckComplexPatMatcher>(M)->Pattern == &Pattern && cast<CheckComplexPatMatcher>(M)->MatchNumber == MatchNumber; } - unsigned getHashImpl() const override { - return (unsigned)(intptr_t)&Pattern ^ MatchNumber; - } }; /// CheckAndImmMatcher - This checks to see if the current node is an 'and' @@ -772,14 +699,11 @@ public: return N->getKind() == CheckAndImm; } - bool isSafeToReorderWithPatternPredicate() const override { return true; } - private: void printImpl(raw_ostream &OS, unsigned indent) const override; bool isEqualImpl(const Matcher *M) const override { return cast<CheckAndImmMatcher>(M)->Value == Value; } - unsigned getHashImpl() const override { return Value; } }; /// CheckOrImmMatcher - This checks to see if the current node is an 'and' @@ -796,14 +720,11 @@ public: return N->getKind() == CheckOrImm; } - bool isSafeToReorderWithPatternPredicate() const override { return true; } - private: void printImpl(raw_ostream &OS, unsigned indent) const override; bool isEqualImpl(const Matcher *M) const override { return cast<CheckOrImmMatcher>(M)->Value == Value; } - unsigned getHashImpl() const override { return Value; } }; /// CheckFoldableChainNodeMatcher - This checks to see if the current node @@ -817,12 +738,9 @@ public: return N->getKind() == CheckFoldableChainNode; } - bool isSafeToReorderWithPatternPredicate() const override { return true; } - private: void printImpl(raw_ostream &OS, unsigned indent) const override; bool isEqualImpl(const Matcher *M) const override { return true; } - unsigned getHashImpl() const override { return 0; } }; /// EmitIntegerMatcher - This creates a new TargetConstant. @@ -846,7 +764,6 @@ private: return cast<EmitIntegerMatcher>(M)->Val == Val && cast<EmitIntegerMatcher>(M)->VT == VT; } - unsigned getHashImpl() const override { return (Val << 4) | VT; } }; /// EmitStringIntegerMatcher - A target constant whose value is represented @@ -871,7 +788,6 @@ private: return cast<EmitStringIntegerMatcher>(M)->Val == Val && cast<EmitStringIntegerMatcher>(M)->VT == VT; } - unsigned getHashImpl() const override; }; /// EmitRegisterMatcher - This creates a new TargetConstant. @@ -897,9 +813,6 @@ private: return cast<EmitRegisterMatcher>(M)->Reg == Reg && cast<EmitRegisterMatcher>(M)->VT == VT; } - unsigned getHashImpl() const override { - return ((unsigned)(intptr_t)Reg) << 4 | VT; - } }; /// EmitConvertToTargetMatcher - Emit an operation that reads a specified @@ -922,7 +835,6 @@ private: bool isEqualImpl(const Matcher *M) const override { return cast<EmitConvertToTargetMatcher>(M)->Slot == Slot; } - unsigned getHashImpl() const override { return Slot; } }; /// EmitMergeInputChainsMatcher - Emit a node that merges a list of input @@ -951,7 +863,6 @@ private: bool isEqualImpl(const Matcher *M) const override { return cast<EmitMergeInputChainsMatcher>(M)->ChainNodes == ChainNodes; } - unsigned getHashImpl() const override; }; /// EmitCopyToRegMatcher - Emit a CopyToReg node from a value to a physreg, @@ -977,9 +888,6 @@ private: return cast<EmitCopyToRegMatcher>(M)->SrcSlot == SrcSlot && cast<EmitCopyToRegMatcher>(M)->DestPhysReg == DestPhysReg; } - unsigned getHashImpl() const override { - return SrcSlot ^ ((unsigned)(intptr_t)DestPhysReg << 4); - } }; @@ -1006,9 +914,6 @@ private: return cast<EmitNodeXFormMatcher>(M)->Slot == Slot && cast<EmitNodeXFormMatcher>(M)->NodeXForm == NodeXForm; } - unsigned getHashImpl() const override { - return Slot ^ ((unsigned)(intptr_t)NodeXForm << 4); - } }; /// EmitNodeMatcherCommon - Common class shared between EmitNode and @@ -1066,7 +971,6 @@ public: private: void printImpl(raw_ostream &OS, unsigned indent) const override; bool isEqualImpl(const Matcher *M) const override; - unsigned getHashImpl() const override; }; /// EmitNodeMatcher - This signals a successful match and generates a node. @@ -1116,34 +1020,6 @@ public: } }; -/// MarkGlueResultsMatcher - This node indicates which non-root nodes in the -/// pattern produce glue. This allows CompleteMatchMatcher to update them -/// with the output glue of the resultant code. -class MarkGlueResultsMatcher : public Matcher { - SmallVector<unsigned, 3> GlueResultNodes; -public: - MarkGlueResultsMatcher(ArrayRef<unsigned> nodes) - : Matcher(MarkGlueResults), GlueResultNodes(nodes.begin(), nodes.end()) {} - - unsigned getNumNodes() const { return GlueResultNodes.size(); } - - unsigned getNode(unsigned i) const { - assert(i < GlueResultNodes.size()); - return GlueResultNodes[i]; - } - - static inline bool classof(const Matcher *N) { - return N->getKind() == MarkGlueResults; - } - -private: - void printImpl(raw_ostream &OS, unsigned indent) const override; - bool isEqualImpl(const Matcher *M) const override { - return cast<MarkGlueResultsMatcher>(M)->GlueResultNodes == GlueResultNodes; - } - unsigned getHashImpl() const override; -}; - /// CompleteMatchMatcher - Complete a match by replacing the results of the /// pattern with the newly generated nodes. This also prints a comment /// indicating the source and dest patterns. @@ -1170,7 +1046,6 @@ private: return cast<CompleteMatchMatcher>(M)->Results == Results && &cast<CompleteMatchMatcher>(M)->Pattern == &Pattern; } - unsigned getHashImpl() const override; }; } // end namespace llvm diff --git a/utils/TableGen/DAGISelMatcherEmitter.cpp b/utils/TableGen/DAGISelMatcherEmitter.cpp index 26f53dca63618..d30fc5131cbaf 100644 --- a/utils/TableGen/DAGISelMatcherEmitter.cpp +++ b/utils/TableGen/DAGISelMatcherEmitter.cpp @@ -247,9 +247,16 @@ EmitMatcher(const Matcher *N, unsigned Indent, unsigned CurrentIdx, OS << "OPC_CaptureGlueInput,\n"; return 1; - case Matcher::MoveChild: - OS << "OPC_MoveChild, " << cast<MoveChildMatcher>(N)->getChildNo() << ",\n"; - return 2; + case Matcher::MoveChild: { + const auto *MCM = cast<MoveChildMatcher>(N); + + OS << "OPC_MoveChild"; + // Handle the specialized forms. + if (MCM->getChildNo() >= 8) + OS << ", "; + OS << MCM->getChildNo() << ",\n"; + return (MCM->getChildNo() >= 8) ? 2 : 1; + } case Matcher::MoveParent: OS << "OPC_MoveParent,\n"; @@ -500,8 +507,8 @@ EmitMatcher(const Matcher *N, unsigned Indent, unsigned CurrentIdx, const EmitMergeInputChainsMatcher *MN = cast<EmitMergeInputChainsMatcher>(N); - // Handle the specialized forms OPC_EmitMergeInputChains1_0 and 1_1. - if (MN->getNumNodes() == 1 && MN->getNode(0) < 2) { + // Handle the specialized forms OPC_EmitMergeInputChains1_0, 1_1, and 1_2. + if (MN->getNumNodes() == 1 && MN->getNode(0) < 3) { OS << "OPC_EmitMergeInputChains1_" << MN->getNode(0) << ",\n"; return 1; } @@ -532,6 +539,10 @@ EmitMatcher(const Matcher *N, unsigned Indent, unsigned CurrentIdx, case Matcher::MorphNodeTo: { const EmitNodeMatcherCommon *EN = cast<EmitNodeMatcherCommon>(N); OS << (isa<EmitNodeMatcher>(EN) ? "OPC_EmitNode" : "OPC_MorphNodeTo"); + bool CompressVTs = EN->getNumVTs() < 3; + if (CompressVTs) + OS << EN->getNumVTs(); + OS << ", TARGET_VAL(" << EN->getOpcodeName() << "), 0"; if (EN->hasChain()) OS << "|OPFL_Chain"; @@ -542,10 +553,13 @@ EmitMatcher(const Matcher *N, unsigned Indent, unsigned CurrentIdx, OS << "|OPFL_Variadic" << EN->getNumFixedArityOperands(); OS << ",\n"; - OS.PadToColumn(Indent*2+4) << EN->getNumVTs(); - if (!OmitComments) - OS << "/*#VTs*/"; - OS << ", "; + OS.PadToColumn(Indent*2+4); + if (!CompressVTs) { + OS << EN->getNumVTs(); + if (!OmitComments) + OS << "/*#VTs*/"; + OS << ", "; + } for (unsigned i = 0, e = EN->getNumVTs(); i != e; ++i) OS << getEnumName(EN->getVT(i)) << ", "; @@ -579,16 +593,7 @@ EmitMatcher(const Matcher *N, unsigned Indent, unsigned CurrentIdx, } else OS << '\n'; - return 6+EN->getNumVTs()+NumOperandBytes; - } - case Matcher::MarkGlueResults: { - const MarkGlueResultsMatcher *CFR = cast<MarkGlueResultsMatcher>(N); - OS << "OPC_MarkGlueResults, " << CFR->getNumNodes() << ", "; - unsigned NumOperandBytes = 0; - for (unsigned i = 0, e = CFR->getNumNodes(); i != e; ++i) - NumOperandBytes += EmitVBRValue(CFR->getNode(i), OS); - OS << '\n'; - return 2+NumOperandBytes; + return 5 + !CompressVTs + EN->getNumVTs() + NumOperandBytes; } case Matcher::CompleteMatch: { const CompleteMatchMatcher *CM = cast<CompleteMatchMatcher>(N); @@ -807,7 +812,6 @@ void MatcherTableEmitter::EmitHistogram(const Matcher *M, case Matcher::EmitNode: OS << "OPC_EmitNode"; break; case Matcher::MorphNodeTo: OS << "OPC_MorphNodeTo"; break; case Matcher::EmitNodeXForm: OS << "OPC_EmitNodeXForm"; break; - case Matcher::MarkGlueResults: OS << "OPC_MarkGlueResults"; break; case Matcher::CompleteMatch: OS << "OPC_CompleteMatch"; break; } @@ -837,8 +841,9 @@ void llvm::EmitMatcherTable(const Matcher *TheMatcher, MatcherEmitter.EmitHistogram(TheMatcher, OS); OS << " #undef TARGET_VAL\n"; - OS << " return SelectCodeCommon(N, MatcherTable,sizeof(MatcherTable));\n}\n"; - OS << '\n'; + OS << " SelectCodeCommon(N, MatcherTable,sizeof(MatcherTable));\n"; + OS << " return nullptr;\n"; + OS << "}\n"; // Next up, emit the function for node and pattern predicates: MatcherEmitter.EmitPredicateFunctions(OS); diff --git a/utils/TableGen/DAGISelMatcherGen.cpp b/utils/TableGen/DAGISelMatcherGen.cpp index 9663b71d6620d..4110e9725b5a8 100644 --- a/utils/TableGen/DAGISelMatcherGen.cpp +++ b/utils/TableGen/DAGISelMatcherGen.cpp @@ -10,7 +10,6 @@ #include "DAGISelMatcher.h" #include "CodeGenDAGPatterns.h" #include "CodeGenRegisters.h" -#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/TableGen/Error.h" @@ -76,10 +75,6 @@ namespace { /// array of all of the recorded input nodes that have chains. SmallVector<unsigned, 2> MatchedChainNodes; - /// MatchedGlueResultNodes - This maintains the position in the recorded - /// nodes array of all of the recorded input nodes that have glue results. - SmallVector<unsigned, 2> MatchedGlueResultNodes; - /// MatchedComplexPatterns - This maintains a list of all of the /// ComplexPatterns that we need to check. The second element of each pair /// is the recorded operand number of the input node. @@ -121,7 +116,7 @@ namespace { /// If this is the first time a node with unique identifier Name has been /// seen, record it. Otherwise, emit a check to make sure this is the same /// node. Returns true if this is the first encounter. - bool recordUniqueNode(std::string Name); + bool recordUniqueNode(const std::string &Name); // Result Code Generation. unsigned getNamedArgumentSlot(StringRef Name) { @@ -426,8 +421,6 @@ void MatcherGen::EmitOperatorMatchCode(const TreePatternNode *N, AddMatcher(new RecordMatcher("'" + N->getOperator()->getName() + "' glue output node", NextRecordedOperandNo)); - // Remember all of the nodes with output glue our pattern will match. - MatchedGlueResultNodes.push_back(NextRecordedOperandNo++); } // If this node is known to have an input glue or if it *might* have an input @@ -445,7 +438,7 @@ void MatcherGen::EmitOperatorMatchCode(const TreePatternNode *N, } } -bool MatcherGen::recordUniqueNode(std::string Name) { +bool MatcherGen::recordUniqueNode(const std::string &Name) { unsigned &VarMapEntry = VariableMap[Name]; if (VarMapEntry == 0) { // If it is a named node, we must emit a 'Record' opcode. @@ -989,11 +982,6 @@ void MatcherGen::EmitResultCode() { assert(Ops.size() >= NumSrcResults && "Didn't provide enough results"); Ops.resize(NumSrcResults); - // If the matched pattern covers nodes which define a glue result, emit a node - // that tells the matcher about them so that it can update their results. - if (!MatchedGlueResultNodes.empty()) - AddMatcher(new MarkGlueResultsMatcher(MatchedGlueResultNodes)); - AddMatcher(new CompleteMatchMatcher(Ops, Pattern)); } diff --git a/utils/TableGen/DAGISelMatcherOpt.cpp b/utils/TableGen/DAGISelMatcherOpt.cpp index c9ee371e3e2fe..ad385fac04389 100644 --- a/utils/TableGen/DAGISelMatcherOpt.cpp +++ b/utils/TableGen/DAGISelMatcherOpt.cpp @@ -13,7 +13,6 @@ #include "DAGISelMatcher.h" #include "CodeGenDAGPatterns.h" -#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" @@ -79,24 +78,6 @@ static void ContractNodes(std::unique_ptr<Matcher> &MatcherPtr, return ContractNodes(MatcherPtr, CGP); } - // Turn EmitNode->MarkFlagResults->CompleteMatch into - // MarkFlagResults->EmitNode->CompleteMatch when we can to encourage - // MorphNodeTo formation. This is safe because MarkFlagResults never refers - // to the root of the pattern. - if (isa<EmitNodeMatcher>(N) && isa<MarkGlueResultsMatcher>(N->getNext()) && - isa<CompleteMatchMatcher>(N->getNext()->getNext())) { - // Unlink the two nodes from the list. - Matcher *EmitNode = MatcherPtr.release(); - Matcher *MFR = EmitNode->takeNext(); - Matcher *Tail = MFR->takeNext(); - - // Relink them. - MatcherPtr.reset(MFR); - MFR->setNext(EmitNode); - EmitNode->setNext(Tail); - return ContractNodes(MatcherPtr, CGP); - } - // Turn EmitNode->CompleteMatch into MorphNodeTo if we can. if (EmitNodeMatcher *EN = dyn_cast<EmitNodeMatcher>(N)) if (CompleteMatchMatcher *CM = @@ -177,59 +158,6 @@ static void ContractNodes(std::unique_ptr<Matcher> &MatcherPtr, } } -/// SinkPatternPredicates - Pattern predicates can be checked at any level of -/// the matching tree. The generator dumps them at the top level of the pattern -/// though, which prevents factoring from being able to see past them. This -/// optimization sinks them as far down into the pattern as possible. -/// -/// Conceptually, we'd like to sink these predicates all the way to the last -/// matcher predicate in the series. However, it turns out that some -/// ComplexPatterns have side effects on the graph, so we really don't want to -/// run a complex pattern if the pattern predicate will fail. For this -/// reason, we refuse to sink the pattern predicate past a ComplexPattern. -/// -static void SinkPatternPredicates(std::unique_ptr<Matcher> &MatcherPtr) { - // Recursively scan for a PatternPredicate. - // If we reached the end of the chain, we're done. - Matcher *N = MatcherPtr.get(); - if (!N) return; - - // Walk down all members of a scope node. - if (ScopeMatcher *Scope = dyn_cast<ScopeMatcher>(N)) { - for (unsigned i = 0, e = Scope->getNumChildren(); i != e; ++i) { - std::unique_ptr<Matcher> Child(Scope->takeChild(i)); - SinkPatternPredicates(Child); - Scope->resetChild(i, Child.release()); - } - return; - } - - // If this node isn't a CheckPatternPredicateMatcher we keep scanning until - // we find one. - CheckPatternPredicateMatcher *CPPM =dyn_cast<CheckPatternPredicateMatcher>(N); - if (!CPPM) - return SinkPatternPredicates(N->getNextPtr()); - - // Ok, we found one, lets try to sink it. Check if we can sink it past the - // next node in the chain. If not, we won't be able to change anything and - // might as well bail. - if (!CPPM->getNext()->isSafeToReorderWithPatternPredicate()) - return; - - // Okay, we know we can sink it past at least one node. Unlink it from the - // chain and scan for the new insertion point. - MatcherPtr.release(); // Don't delete CPPM. - MatcherPtr.reset(CPPM->takeNext()); - - N = MatcherPtr.get(); - while (N->getNext()->isSafeToReorderWithPatternPredicate()) - N = N->getNext(); - - // At this point, we want to insert CPPM after N. - CPPM->setNext(N->takeNext()); - N->setNext(CPPM); -} - /// FindNodeWithKind - Scan a series of matchers looking for a matcher with a /// specified kind. Return null if we didn't find one otherwise return the /// matcher. @@ -264,8 +192,7 @@ static void FactorNodes(std::unique_ptr<Matcher> &MatcherPtr) { return FactorNodes(N->getNextPtr()); // Okay, pull together the children of the scope node into a vector so we can - // inspect it more easily. While we're at it, bucket them up by the hash - // code of their first predicate. + // inspect it more easily. SmallVector<Matcher*, 32> OptionsToMatch; for (unsigned i = 0, e = Scope->getNumChildren(); i != e; ++i) { @@ -456,7 +383,8 @@ static void FactorNodes(std::unique_ptr<Matcher> &MatcherPtr) { CheckOpcodeMatcher *COM = cast<CheckOpcodeMatcher>(NewOptionsToMatch[i]); assert(Opcodes.insert(COM->getOpcode().getEnumName()).second && "Duplicate opcodes not factored?"); - Cases.push_back(std::make_pair(&COM->getOpcode(), COM->getNext())); + Cases.push_back(std::make_pair(&COM->getOpcode(), COM->takeNext())); + delete COM; } MatcherPtr.reset(new SwitchOpcodeMatcher(Cases)); @@ -486,7 +414,9 @@ static void FactorNodes(std::unique_ptr<Matcher> &MatcherPtr) { } Matcher *Entries[2] = { PrevMatcher, MatcherWithoutCTM }; - Cases[Entry-1].second = new ScopeMatcher(Entries); + std::unique_ptr<Matcher> Case(new ScopeMatcher(Entries)); + FactorNodes(Case); + Cases[Entry-1].second = Case.release(); continue; } @@ -515,6 +445,5 @@ void llvm::OptimizeMatcher(std::unique_ptr<Matcher> &MatcherPtr, const CodeGenDAGPatterns &CGP) { ContractNodes(MatcherPtr, CGP); - SinkPatternPredicates(MatcherPtr); FactorNodes(MatcherPtr); } diff --git a/utils/TableGen/DFAPacketizerEmitter.cpp b/utils/TableGen/DFAPacketizerEmitter.cpp index 77afff7ab5c23..e31caaf3c98c6 100644 --- a/utils/TableGen/DFAPacketizerEmitter.cpp +++ b/utils/TableGen/DFAPacketizerEmitter.cpp @@ -1,4 +1,4 @@ -//===- DFAPacketizerEmitter.cpp - Packetization DFA for a VLIW machine-----===// +//===- DFAPacketizerEmitter.cpp - Packetization DFA for a VLIW machine ----===// // // The LLVM Compiler Infrastructure // @@ -24,10 +24,10 @@ #include "llvm/TableGen/Record.h" #include "llvm/TableGen/TableGenBackend.h" #include "llvm/Support/Debug.h" -#include <list> #include <map> #include <string> #include <queue> + using namespace llvm; // -------------------------------------------------------------------- @@ -73,7 +73,8 @@ namespace { InsnInput = addDFAFuncUnits(InsnInput, U); return InsnInput; } -} +} // end anonymous namespace + // -------------------------------------------------------------------- #ifndef NDEBUG @@ -149,7 +150,7 @@ public: void run(raw_ostream &OS); }; -} // End anonymous namespace. +} // end anonymous namespace // // @@ -234,7 +235,7 @@ class State { // bool hasTransition(std::vector<unsigned> InsnClass) const; }; -} // End anonymous namespace. +} // end anonymous namespace // // class DFA: deterministic finite automaton for processor resource tracking. @@ -262,7 +263,7 @@ public: int numInsnClasses = 0, int maxResources = 0, int numCombos = 0, int maxStages = 0); }; -} // End anonymous namespace. +} // end anonymous namespace #ifndef NDEBUG // To enable debugging, run llvm-tblgen with: "-debug-only dfa-emitter". @@ -305,7 +306,7 @@ void dbgsIndent(unsigned indent) { DEBUG(dbgs() << " "); } } -#endif +#endif // NDEBUG // // Constructors and destructors for State and DFA @@ -454,7 +455,6 @@ void State::AddInsnClassStages(std::vector<unsigned> &InsnClass, } } - // // canMaybeAddInsnClass - Quickly verifies if an instruction of type InsnClass // may be a valid transition from this state i.e., can an instruction of type @@ -505,7 +505,6 @@ bool State::canMaybeAddInsnClass(std::vector<unsigned> &InsnClass, return false; } - const State &DFA::newState() { auto IterPair = states.insert(State()); assert(IterPair.second && "State already exists"); @@ -518,7 +517,6 @@ DFAPacketizerEmitter::DFAPacketizerEmitter(RecordKeeper &R): TargetName(CodeGenTarget(R).getName()), allInsnClasses(), Records(R) {} - // // writeTableAndAPI - Print out a table representing the DFA and the // associated API to create a DFA packetizer. @@ -626,7 +624,6 @@ void DFA::writeTableAndAPI(raw_ostream &OS, const std::string &TargetName, OS << "};\n"; OS << "} // namespace\n"; - // // Emit DFA Packetizer tables if the target is a VLIW machine. // @@ -640,7 +637,6 @@ void DFA::writeTableAndAPI(raw_ostream &OS, const std::string &TargetName, OS << "} // End llvm namespace \n"; } - // // collectAllFuncUnits - Construct a map of function unit names to bits. // @@ -713,7 +709,7 @@ int DFAPacketizerEmitter::collectAllComboFuncs( Record *ComboFunc = FuncData->getValueAsDef("TheComboFunc"); const std::vector<Record*> &FuncList = FuncData->getValueAsListOfDefs("FuncList"); - std::string ComboFuncName = ComboFunc->getName(); + const std::string &ComboFuncName = ComboFunc->getName(); unsigned ComboBit = FUNameToBitsMap[ComboFuncName]; unsigned ComboResources = ComboBit; DEBUG(dbgs() << " combo: " << ComboFuncName @@ -735,7 +731,6 @@ int DFAPacketizerEmitter::collectAllComboFuncs( return numCombos; } - // // collectOneInsnClass - Populate allInsnClasses with one instruction class // @@ -940,7 +935,7 @@ void DFAPacketizerEmitter::run(raw_ostream &OS) { // if (!current->hasTransition(InsnClass) && current->canMaybeAddInsnClass(InsnClass, ComboBitToBitsMap)) { - const State *NewState = NULL; + const State *NewState = nullptr; current->AddInsnClass(InsnClass, ComboBitToBitsMap, NewStateResources); if (NewStateResources.size() == 0) { DEBUG(dbgs() << " Skipped - no new states generated\n"); @@ -994,4 +989,4 @@ void EmitDFAPacketizer(RecordKeeper &RK, raw_ostream &OS) { DFAPacketizerEmitter(RK).run(OS); } -} // End llvm namespace +} // end namespaec llvm diff --git a/utils/TableGen/DisassemblerEmitter.cpp b/utils/TableGen/DisassemblerEmitter.cpp index e8595271bccef..44815b05f274f 100644 --- a/utils/TableGen/DisassemblerEmitter.cpp +++ b/utils/TableGen/DisassemblerEmitter.cpp @@ -96,12 +96,11 @@ using namespace llvm::X86Disassembler; namespace llvm { extern void EmitFixedLenDecoder(RecordKeeper &RK, raw_ostream &OS, - std::string PredicateNamespace, - std::string GPrefix, - std::string GPostfix, - std::string ROK, - std::string RFail, - std::string L); + const std::string &PredicateNamespace, + const std::string &GPrefix, + const std::string &GPostfix, + const std::string &ROK, + const std::string &RFail, const std::string &L); void EmitDisassembler(RecordKeeper &Records, raw_ostream &OS) { CodeGenTarget Target(Records); @@ -111,7 +110,7 @@ void EmitDisassembler(RecordKeeper &Records, raw_ostream &OS) { if (Target.getName() == "X86") { DisassemblerTables Tables; - const std::vector<const CodeGenInstruction*> &numberedInstructions = + ArrayRef<const CodeGenInstruction*> numberedInstructions = Target.getInstructionsByEnumValue(); for (unsigned i = 0, e = numberedInstructions.size(); i != e; ++i) diff --git a/utils/TableGen/FastISelEmitter.cpp b/utils/TableGen/FastISelEmitter.cpp index 748c923477838..debb12c4f5110 100644 --- a/utils/TableGen/FastISelEmitter.cpp +++ b/utils/TableGen/FastISelEmitter.cpp @@ -18,13 +18,13 @@ //===----------------------------------------------------------------------===// #include "CodeGenDAGPatterns.h" -#include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/TableGen/Error.h" #include "llvm/TableGen/Record.h" #include "llvm/TableGen/TableGenBackend.h" +#include <utility> using namespace llvm; @@ -417,9 +417,7 @@ static std::string getLegalCName(std::string OpName) { return OpName; } -FastISelMap::FastISelMap(std::string instns) - : InstNS(instns) { -} +FastISelMap::FastISelMap(std::string instns) : InstNS(std::move(instns)) {} static std::string PhyRegForNode(TreePatternNode *Op, const CodeGenTarget &Target) { diff --git a/utils/TableGen/FixedLenDecoderEmitter.cpp b/utils/TableGen/FixedLenDecoderEmitter.cpp index 8ca4a1bf54040..0506400b90f65 100644 --- a/utils/TableGen/FixedLenDecoderEmitter.cpp +++ b/utils/TableGen/FixedLenDecoderEmitter.cpp @@ -28,6 +28,7 @@ #include "llvm/TableGen/Record.h" #include <map> #include <string> +#include <utility> #include <vector> using namespace llvm; @@ -47,7 +48,7 @@ struct OperandInfo { bool HasCompleteDecoder; OperandInfo(std::string D, bool HCD) - : Decoder(D), HasCompleteDecoder(HCD) { } + : Decoder(std::move(D)), HasCompleteDecoder(HCD) {} void addField(unsigned Base, unsigned Width, unsigned Offset) { Fields.push_back(EncodingField(Base, Width, Offset)); @@ -78,22 +79,21 @@ struct DecoderTableInfo { namespace { class FixedLenDecoderEmitter { - const std::vector<const CodeGenInstruction*> *NumberedInstructions; + ArrayRef<const CodeGenInstruction *> NumberedInstructions; public: // Defaults preserved here for documentation, even though they aren't // strictly necessary given the way that this is currently being called. - FixedLenDecoderEmitter(RecordKeeper &R, - std::string PredicateNamespace, - std::string GPrefix = "if (", + FixedLenDecoderEmitter(RecordKeeper &R, std::string PredicateNamespace, + std::string GPrefix = "if (", std::string GPostfix = " == MCDisassembler::Fail)", - std::string ROK = "MCDisassembler::Success", - std::string RFail = "MCDisassembler::Fail", - std::string L = "") : - Target(R), - PredicateNamespace(PredicateNamespace), - GuardPrefix(GPrefix), GuardPostfix(GPostfix), - ReturnOK(ROK), ReturnFail(RFail), Locals(L) {} + std::string ROK = "MCDisassembler::Success", + std::string RFail = "MCDisassembler::Fail", + std::string L = "") + : Target(R), PredicateNamespace(std::move(PredicateNamespace)), + GuardPrefix(std::move(GPrefix)), GuardPostfix(std::move(GPostfix)), + ReturnOK(std::move(ROK)), ReturnFail(std::move(RFail)), + Locals(std::move(L)) {} // Emit the decoder state machine table. void emitTable(formatted_raw_ostream &o, DecoderTable &Table, @@ -306,7 +306,7 @@ protected: friend class Filter; // Vector of codegen instructions to choose our filter. - const std::vector<const CodeGenInstruction*> &AllInstructions; + ArrayRef<const CodeGenInstruction *> AllInstructions; // Vector of uid's for this filter chooser to work on. const std::vector<unsigned> &Opcodes; @@ -337,7 +337,7 @@ protected: void operator=(const FilterChooser &) = delete; public: - FilterChooser(const std::vector<const CodeGenInstruction*> &Insts, + FilterChooser(ArrayRef<const CodeGenInstruction *> Insts, const std::vector<unsigned> &IDs, const std::map<unsigned, std::vector<OperandInfo> > &Ops, unsigned BW, @@ -348,7 +348,7 @@ public: doFilter(); } - FilterChooser(const std::vector<const CodeGenInstruction*> &Insts, + FilterChooser(ArrayRef<const CodeGenInstruction *> Insts, const std::vector<unsigned> &IDs, const std::map<unsigned, std::vector<OperandInfo> > &Ops, const std::vector<bit_value_t> &ParentFilterBitValues, @@ -410,9 +410,6 @@ protected: return Filters[BestIndex]; } - // Called from Filter::recurse() when singleton exists. For debug purpose. - void SingletonExists(unsigned Opc) const; - bool PositionFiltered(unsigned i) const { return ValueSet(FilterBitValues[i]); } @@ -559,7 +556,6 @@ void Filter::recurse() { // No need to recurse for a singleton filtered instruction. // See also Filter::emit*(). if (getNumFiltered() == 1) { - //Owner->SingletonExists(LastOpcFiltered); assert(FilterChooserMap.size() == 1); return; } @@ -732,15 +728,15 @@ void FixedLenDecoderEmitter::emitTable(formatted_raw_ostream &OS, OS.indent(Indentation) << "MCD::OPC_FilterValue, "; // The filter value is ULEB128 encoded. while (*I >= 128) - OS << utostr(*I++) << ", "; - OS << utostr(*I++) << ", "; + OS << (unsigned)*I++ << ", "; + OS << (unsigned)*I++ << ", "; // 16-bit numtoskip value. uint8_t Byte = *I++; uint32_t NumToSkip = Byte; - OS << utostr(Byte) << ", "; + OS << (unsigned)Byte << ", "; Byte = *I++; - OS << utostr(Byte) << ", "; + OS << (unsigned)Byte << ", "; NumToSkip |= Byte << 8; OS << "// Skip to: " << ((I - Table.begin()) + NumToSkip) << "\n"; break; @@ -753,14 +749,14 @@ void FixedLenDecoderEmitter::emitTable(formatted_raw_ostream &OS, << Len << ", ";// << Val << ", " << NumToSkip << ",\n"; // ULEB128 encoded field value. for (; *I >= 128; ++I) - OS << utostr(*I) << ", "; - OS << utostr(*I++) << ", "; + OS << (unsigned)*I << ", "; + OS << (unsigned)*I++ << ", "; // 16-bit numtoskip value. uint8_t Byte = *I++; uint32_t NumToSkip = Byte; - OS << utostr(Byte) << ", "; + OS << (unsigned)Byte << ", "; Byte = *I++; - OS << utostr(Byte) << ", "; + OS << (unsigned)Byte << ", "; NumToSkip |= Byte << 8; OS << "// Skip to: " << ((I - Table.begin()) + NumToSkip) << "\n"; break; @@ -769,15 +765,15 @@ void FixedLenDecoderEmitter::emitTable(formatted_raw_ostream &OS, ++I; OS.indent(Indentation) << "MCD::OPC_CheckPredicate, "; for (; *I >= 128; ++I) - OS << utostr(*I) << ", "; - OS << utostr(*I++) << ", "; + OS << (unsigned)*I << ", "; + OS << (unsigned)*I++ << ", "; // 16-bit numtoskip value. uint8_t Byte = *I++; uint32_t NumToSkip = Byte; - OS << utostr(Byte) << ", "; + OS << (unsigned)Byte << ", "; Byte = *I++; - OS << utostr(Byte) << ", "; + OS << (unsigned)Byte << ", "; NumToSkip |= Byte << 8; OS << "// Skip to: " << ((I - Table.begin()) + NumToSkip) << "\n"; break; @@ -796,17 +792,17 @@ void FixedLenDecoderEmitter::emitTable(formatted_raw_ostream &OS, OS.indent(Indentation) << "MCD::OPC_" << (IsTry ? "Try" : "") << "Decode, "; for (p = Buffer; *p >= 128; ++p) - OS << utostr(*p) << ", "; - OS << utostr(*p) << ", "; + OS << (unsigned)*p << ", "; + OS << (unsigned)*p << ", "; // Decoder index. for (; *I >= 128; ++I) - OS << utostr(*I) << ", "; - OS << utostr(*I++) << ", "; + OS << (unsigned)*I << ", "; + OS << (unsigned)*I++ << ", "; if (!IsTry) { OS << "// Opcode: " - << NumberedInstructions->at(Opc)->TheDef->getName() << "\n"; + << NumberedInstructions[Opc]->TheDef->getName() << "\n"; break; } @@ -815,13 +811,13 @@ void FixedLenDecoderEmitter::emitTable(formatted_raw_ostream &OS, // 16-bit numtoskip value. uint8_t Byte = *I++; uint32_t NumToSkip = Byte; - OS << utostr(Byte) << ", "; + OS << (unsigned)Byte << ", "; Byte = *I++; - OS << utostr(Byte) << ", "; + OS << (unsigned)Byte << ", "; NumToSkip |= Byte << 8; OS << "// Opcode: " - << NumberedInstructions->at(Opc)->TheDef->getName() + << NumberedInstructions[Opc]->TheDef->getName() << ", skip to: " << ((I - Table.begin()) + NumToSkip) << "\n"; break; } @@ -832,22 +828,28 @@ void FixedLenDecoderEmitter::emitTable(formatted_raw_ostream &OS, uint64_t Value = 0; unsigned Shift = 0; do { - OS << ", " << utostr(*I); + OS << ", " << (unsigned)*I; Value += (*I & 0x7f) << Shift; Shift += 7; } while (*I++ >= 128); - if (Value > 127) - OS << " /* 0x" << utohexstr(Value) << " */"; + if (Value > 127) { + OS << " /* 0x"; + OS.write_hex(Value); + OS << " */"; + } // Negative mask Value = 0; Shift = 0; do { - OS << ", " << utostr(*I); + OS << ", " << (unsigned)*I; Value += (*I & 0x7f) << Shift; Shift += 7; } while (*I++ >= 128); - if (Value > 127) - OS << " /* 0x" << utohexstr(Value) << " */"; + if (Value > 127) { + OS << " /* 0x"; + OS.write_hex(Value); + OS << " */"; + } OS << ",\n"; break; } @@ -971,30 +973,6 @@ void FilterChooser::dumpStack(raw_ostream &o, const char *prefix) const { } } -// Called from Filter::recurse() when singleton exists. For debug purpose. -void FilterChooser::SingletonExists(unsigned Opc) const { - insn_t Insn0; - insnWithID(Insn0, Opc); - - errs() << "Singleton exists: " << nameWithID(Opc) - << " with its decoding dominating "; - for (unsigned i = 0; i < Opcodes.size(); ++i) { - if (Opcodes[i] == Opc) continue; - errs() << nameWithID(Opcodes[i]) << ' '; - } - errs() << '\n'; - - dumpStack(errs(), "\t\t"); - for (unsigned i = 0; i < Opcodes.size(); ++i) { - const std::string &Name = nameWithID(Opcodes[i]); - - errs() << '\t' << Name << " "; - dumpBits(errs(), - getBitsField(*AllInstructions[Opcodes[i]]->TheDef, "Inst")); - errs() << '\n'; - } -} - // Calculates the island(s) needed to decode the instruction. // This returns a list of undecoded bits of an instructions, for example, // Inst{20} = 1 && Inst{3-0} == 0b1111 represents two islands of yet-to-be @@ -2250,13 +2228,13 @@ void FixedLenDecoderEmitter::run(raw_ostream &o) { Target.reverseBitsForLittleEndianEncoding(); // Parameterize the decoders based on namespace and instruction width. - NumberedInstructions = &Target.getInstructionsByEnumValue(); + NumberedInstructions = Target.getInstructionsByEnumValue(); std::map<std::pair<std::string, unsigned>, std::vector<unsigned> > OpcMap; std::map<unsigned, std::vector<OperandInfo> > Operands; - for (unsigned i = 0; i < NumberedInstructions->size(); ++i) { - const CodeGenInstruction *Inst = NumberedInstructions->at(i); + for (unsigned i = 0; i < NumberedInstructions.size(); ++i) { + const CodeGenInstruction *Inst = NumberedInstructions[i]; const Record *Def = Inst->TheDef; unsigned Size = Def->getValueAsInt("Size"); if (Def->getValueAsString("Namespace") == "TargetOpcode" || @@ -2277,7 +2255,7 @@ void FixedLenDecoderEmitter::run(raw_ostream &o) { DecoderTableInfo TableInfo; for (const auto &Opc : OpcMap) { // Emit the decoder for this namespace+width combination. - FilterChooser FC(*NumberedInstructions, Opc.second, Operands, + FilterChooser FC(NumberedInstructions, Opc.second, Operands, 8*Opc.first.second, this); // The decode table is cleared for each top level decoder function. The @@ -2318,12 +2296,10 @@ void FixedLenDecoderEmitter::run(raw_ostream &o) { namespace llvm { void EmitFixedLenDecoder(RecordKeeper &RK, raw_ostream &OS, - std::string PredicateNamespace, - std::string GPrefix, - std::string GPostfix, - std::string ROK, - std::string RFail, - std::string L) { + const std::string &PredicateNamespace, + const std::string &GPrefix, + const std::string &GPostfix, const std::string &ROK, + const std::string &RFail, const std::string &L) { FixedLenDecoderEmitter(RK, PredicateNamespace, GPrefix, GPostfix, ROK, RFail, L).run(OS); } diff --git a/utils/TableGen/InstrInfoEmitter.cpp b/utils/TableGen/InstrInfoEmitter.cpp index a6583399fa209..02461cc0508dd 100644 --- a/utils/TableGen/InstrInfoEmitter.cpp +++ b/utils/TableGen/InstrInfoEmitter.cpp @@ -59,12 +59,12 @@ private: raw_ostream &OS); void emitOperandTypesEnum(raw_ostream &OS, const CodeGenTarget &Target); void initOperandMapData( - const std::vector<const CodeGenInstruction *> &NumberedInstructions, + ArrayRef<const CodeGenInstruction *> NumberedInstructions, const std::string &Namespace, std::map<std::string, unsigned> &Operands, OpNameMapTy &OperandMap); void emitOperandNameMappings(raw_ostream &OS, const CodeGenTarget &Target, - const std::vector<const CodeGenInstruction*> &NumberedInstructions); + ArrayRef<const CodeGenInstruction*> NumberedInstructions); // Operand information. void EmitOperandInfo(raw_ostream &OS, OperandInfoMapTy &OperandInfoIDs); @@ -75,8 +75,8 @@ private: static void PrintDefList(const std::vector<Record*> &Uses, unsigned Num, raw_ostream &OS) { OS << "static const MCPhysReg ImplicitList" << Num << "[] = { "; - for (unsigned i = 0, e = Uses.size(); i != e; ++i) - OS << getQualifiedName(Uses[i]) << ", "; + for (Record *U : Uses) + OS << getQualifiedName(U) << ", "; OS << "0 };\n"; } @@ -177,7 +177,7 @@ void InstrInfoEmitter::EmitOperandInfo(raw_ostream &OS, OS << "\n"; const CodeGenTarget &Target = CDP.getTargetInfo(); - for (const CodeGenInstruction *Inst : Target.instructions()) { + for (const CodeGenInstruction *Inst : Target.getInstructionsByEnumValue()) { std::vector<std::string> OperandInfo = GetOperandInfo(*Inst); unsigned &N = OperandInfoIDs[OperandInfo]; if (N != 0) continue; @@ -198,7 +198,7 @@ void InstrInfoEmitter::EmitOperandInfo(raw_ostream &OS, /// each instructions. This is used to generate the OperandMap table as /// well as the getNamedOperandIdx() function. void InstrInfoEmitter::initOperandMapData( - const std::vector<const CodeGenInstruction *> &NumberedInstructions, + ArrayRef<const CodeGenInstruction *> NumberedInstructions, const std::string &Namespace, std::map<std::string, unsigned> &Operands, OpNameMapTy &OperandMap) { @@ -234,7 +234,7 @@ void InstrInfoEmitter::initOperandMapData( /// OpName enum void InstrInfoEmitter::emitOperandNameMappings(raw_ostream &OS, const CodeGenTarget &Target, - const std::vector<const CodeGenInstruction*> &NumberedInstructions) { + ArrayRef<const CodeGenInstruction*> NumberedInstructions) { const std::string &Namespace = Target.getInstNamespace(); std::string OpNameNS = "OpName"; @@ -249,7 +249,7 @@ void InstrInfoEmitter::emitOperandNameMappings(raw_ostream &OS, OS << "#undef GET_INSTRINFO_OPERAND_ENUM\n"; OS << "namespace llvm {\n"; OS << "namespace " << Namespace << " {\n"; - OS << "namespace " << OpNameNS << " { \n"; + OS << "namespace " << OpNameNS << " {\n"; OS << "enum {\n"; for (const auto &Op : Operands) OS << " " << Op.first << " = " << Op.second << ",\n"; @@ -259,7 +259,7 @@ void InstrInfoEmitter::emitOperandNameMappings(raw_ostream &OS, OS << "} // end namespace OpName\n"; OS << "} // end namespace " << Namespace << "\n"; OS << "} // end namespace llvm\n"; - OS << "#endif //GET_INSTRINFO_OPERAND_ENUM\n"; + OS << "#endif //GET_INSTRINFO_OPERAND_ENUM\n\n"; OS << "#ifdef GET_INSTRINFO_NAMED_OPS\n"; OS << "#undef GET_INSTRINFO_NAMED_OPS\n"; @@ -299,7 +299,7 @@ void InstrInfoEmitter::emitOperandNameMappings(raw_ostream &OS, OS << "}\n"; OS << "} // end namespace " << Namespace << "\n"; OS << "} // end namespace llvm\n"; - OS << "#endif //GET_INSTRINFO_NAMED_OPS\n"; + OS << "#endif //GET_INSTRINFO_NAMED_OPS\n\n"; } @@ -312,11 +312,11 @@ void InstrInfoEmitter::emitOperandTypesEnum(raw_ostream &OS, const std::string &Namespace = Target.getInstNamespace(); std::vector<Record *> Operands = Records.getAllDerivedDefinitions("Operand"); - OS << "\n#ifdef GET_INSTRINFO_OPERAND_TYPES_ENUM\n"; + OS << "#ifdef GET_INSTRINFO_OPERAND_TYPES_ENUM\n"; OS << "#undef GET_INSTRINFO_OPERAND_TYPES_ENUM\n"; OS << "namespace llvm {\n"; OS << "namespace " << Namespace << " {\n"; - OS << "namespace OpTypes { \n"; + OS << "namespace OpTypes {\n"; OS << "enum OperandType {\n"; unsigned EnumVal = 0; @@ -330,7 +330,7 @@ void InstrInfoEmitter::emitOperandTypesEnum(raw_ostream &OS, OS << "} // end namespace OpTypes\n"; OS << "} // end namespace " << Namespace << "\n"; OS << "} // end namespace llvm\n"; - OS << "#endif // GET_INSTRINFO_OPERAND_TYPES_ENUM\n"; + OS << "#endif // GET_INSTRINFO_OPERAND_TYPES_ENUM\n\n"; } //===----------------------------------------------------------------------===// @@ -339,12 +339,10 @@ void InstrInfoEmitter::emitOperandTypesEnum(raw_ostream &OS, // run - Emit the main instruction description records for the target... void InstrInfoEmitter::run(raw_ostream &OS) { - emitSourceFileHeader("Target Instruction Enum Values", OS); + emitSourceFileHeader("Target Instruction Enum Values and Descriptors", OS); emitEnums(OS); - emitSourceFileHeader("Target Instruction Descriptors", OS); - - OS << "\n#ifdef GET_INSTRINFO_MC_DESC\n"; + OS << "#ifdef GET_INSTRINFO_MC_DESC\n"; OS << "#undef GET_INSTRINFO_MC_DESC\n"; OS << "namespace llvm {\n\n"; @@ -358,7 +356,7 @@ void InstrInfoEmitter::run(raw_ostream &OS) { unsigned ListNumber = 0; // Emit all of the instruction's implicit uses and defs. - for (const CodeGenInstruction *II : Target.instructions()) { + for (const CodeGenInstruction *II : Target.getInstructionsByEnumValue()) { Record *Inst = II->TheDef; std::vector<Record*> Uses = Inst->getValueAsListOfDefs("Uses"); if (!Uses.empty()) { @@ -380,7 +378,7 @@ void InstrInfoEmitter::run(raw_ostream &OS) { // Emit all of the MCInstrDesc records in their ENUM ordering. // OS << "\nextern const MCInstrDesc " << TargetName << "Insts[] = {\n"; - const std::vector<const CodeGenInstruction*> &NumberedInstructions = + ArrayRef<const CodeGenInstruction*> NumberedInstructions = Target.getInstructionsByEnumValue(); SequenceToOffsetTable<std::string> InstrNames; @@ -418,26 +416,26 @@ void InstrInfoEmitter::run(raw_ostream &OS) { << TargetName << "InstrNameIndices, " << TargetName << "InstrNameData, " << NumberedInstructions.size() << ");\n}\n\n"; - OS << "} // end llvm namespace \n"; + OS << "} // end llvm namespace\n"; OS << "#endif // GET_INSTRINFO_MC_DESC\n\n"; // Create a TargetInstrInfo subclass to hide the MC layer initialization. - OS << "\n#ifdef GET_INSTRINFO_HEADER\n"; + OS << "#ifdef GET_INSTRINFO_HEADER\n"; OS << "#undef GET_INSTRINFO_HEADER\n"; std::string ClassName = TargetName + "GenInstrInfo"; OS << "namespace llvm {\n"; OS << "struct " << ClassName << " : public TargetInstrInfo {\n" << " explicit " << ClassName - << "(int CFSetupOpcode = -1, int CFDestroyOpcode = -1, int CatchRetOpcode = -1);\n" + << "(int CFSetupOpcode = -1, int CFDestroyOpcode = -1, int CatchRetOpcode = -1, int ReturnOpcode = -1);\n" << " ~" << ClassName << "() override {}\n" << "};\n"; - OS << "} // end llvm namespace \n"; + OS << "} // end llvm namespace\n"; OS << "#endif // GET_INSTRINFO_HEADER\n\n"; - OS << "\n#ifdef GET_INSTRINFO_CTOR_DTOR\n"; + OS << "#ifdef GET_INSTRINFO_CTOR_DTOR\n"; OS << "#undef GET_INSTRINFO_CTOR_DTOR\n"; OS << "namespace llvm {\n"; @@ -445,12 +443,12 @@ void InstrInfoEmitter::run(raw_ostream &OS) { OS << "extern const unsigned " << TargetName << "InstrNameIndices[];\n"; OS << "extern const char " << TargetName << "InstrNameData[];\n"; OS << ClassName << "::" << ClassName - << "(int CFSetupOpcode, int CFDestroyOpcode, int CatchRetOpcode)\n" - << " : TargetInstrInfo(CFSetupOpcode, CFDestroyOpcode, CatchRetOpcode) {\n" + << "(int CFSetupOpcode, int CFDestroyOpcode, int CatchRetOpcode, int ReturnOpcode)\n" + << " : TargetInstrInfo(CFSetupOpcode, CFDestroyOpcode, CatchRetOpcode, ReturnOpcode) {\n" << " InitMCInstrInfo(" << TargetName << "Insts, " << TargetName << "InstrNameIndices, " << TargetName << "InstrNameData, " << NumberedInstructions.size() << ");\n}\n"; - OS << "} // end llvm namespace \n"; + OS << "} // end llvm namespace\n"; OS << "#endif // GET_INSTRINFO_CTOR_DTOR\n\n"; @@ -564,7 +562,7 @@ void InstrInfoEmitter::emitRecord(const CodeGenInstruction &Inst, unsigned Num, // emitEnums - Print out enum values for all of the instructions. void InstrInfoEmitter::emitEnums(raw_ostream &OS) { - OS << "\n#ifdef GET_INSTRINFO_ENUM\n"; + OS << "#ifdef GET_INSTRINFO_ENUM\n"; OS << "#undef GET_INSTRINFO_ENUM\n"; OS << "namespace llvm {\n\n"; @@ -577,26 +575,23 @@ void InstrInfoEmitter::emitEnums(raw_ostream &OS) { if (Namespace.empty()) PrintFatalError("No instructions defined!"); - const std::vector<const CodeGenInstruction*> &NumberedInstructions = - Target.getInstructionsByEnumValue(); - OS << "namespace " << Namespace << " {\n"; OS << " enum {\n"; unsigned Num = 0; - for (const CodeGenInstruction *Inst : NumberedInstructions) + for (const CodeGenInstruction *Inst : Target.getInstructionsByEnumValue()) OS << " " << Inst->TheDef->getName() << "\t= " << Num++ << ",\n"; - OS << " INSTRUCTION_LIST_END = " << NumberedInstructions.size() << "\n"; + OS << " INSTRUCTION_LIST_END = " << Num << "\n"; OS << " };\n\n"; OS << "namespace Sched {\n"; OS << " enum {\n"; Num = 0; for (const auto &Class : SchedModels.explicit_classes()) OS << " " << Class.Name << "\t= " << Num++ << ",\n"; - OS << " SCHED_LIST_END = " << SchedModels.numInstrSchedClasses() << "\n"; + OS << " SCHED_LIST_END = " << Num << "\n"; OS << " };\n"; OS << "} // end Sched namespace\n"; OS << "} // end " << Namespace << " namespace\n"; - OS << "} // end llvm namespace \n"; + OS << "} // end llvm namespace\n"; OS << "#endif // GET_INSTRINFO_ENUM\n\n"; } diff --git a/utils/TableGen/IntrinsicEmitter.cpp b/utils/TableGen/IntrinsicEmitter.cpp index 42a6a152f55ec..a676159e494b9 100644 --- a/utils/TableGen/IntrinsicEmitter.cpp +++ b/utils/TableGen/IntrinsicEmitter.cpp @@ -20,6 +20,7 @@ #include "llvm/TableGen/Record.h" #include "llvm/TableGen/StringMatcher.h" #include "llvm/TableGen/TableGenBackend.h" +#include "llvm/TableGen/StringToOffsetTable.h" #include <algorithm> using namespace llvm; @@ -37,23 +38,16 @@ public: void EmitPrefix(raw_ostream &OS); - void EmitEnumInfo(const std::vector<CodeGenIntrinsic> &Ints, - raw_ostream &OS); - - void EmitFnNameRecognizer(const std::vector<CodeGenIntrinsic> &Ints, - raw_ostream &OS); - void EmitIntrinsicToNameTable(const std::vector<CodeGenIntrinsic> &Ints, + void EmitEnumInfo(const CodeGenIntrinsicTable &Ints, raw_ostream &OS); + void EmitTargetInfo(const CodeGenIntrinsicTable &Ints, raw_ostream &OS); + void EmitIntrinsicToNameTable(const CodeGenIntrinsicTable &Ints, raw_ostream &OS); - void EmitIntrinsicToOverloadTable(const std::vector<CodeGenIntrinsic> &Ints, - raw_ostream &OS); - void EmitGenerator(const std::vector<CodeGenIntrinsic> &Ints, - raw_ostream &OS); - void EmitAttributes(const std::vector<CodeGenIntrinsic> &Ints, - raw_ostream &OS); - void EmitIntrinsicToGCCBuiltinMap(const std::vector<CodeGenIntrinsic> &Ints, + void EmitIntrinsicToOverloadTable(const CodeGenIntrinsicTable &Ints, raw_ostream &OS); - void EmitIntrinsicToMSBuiltinMap(const std::vector<CodeGenIntrinsic> &Ints, - raw_ostream &OS); + void EmitGenerator(const CodeGenIntrinsicTable &Ints, raw_ostream &OS); + void EmitAttributes(const CodeGenIntrinsicTable &Ints, raw_ostream &OS); + void EmitIntrinsicToBuiltinMap(const CodeGenIntrinsicTable &Ints, bool IsGCC, + raw_ostream &OS); void EmitSuffix(raw_ostream &OS); }; } // End anonymous namespace @@ -65,7 +59,7 @@ public: void IntrinsicEmitter::run(raw_ostream &OS) { emitSourceFileHeader("Intrinsic Function Source Fragment", OS); - std::vector<CodeGenIntrinsic> Ints = LoadIntrinsics(Records, TargetOnly); + CodeGenIntrinsicTable Ints(Records, TargetOnly); if (TargetOnly && !Ints.empty()) TargetPrefix = Ints[0].TargetPrefix; @@ -75,26 +69,29 @@ void IntrinsicEmitter::run(raw_ostream &OS) { // Emit the enum information. EmitEnumInfo(Ints, OS); + // Emit the target metadata. + EmitTargetInfo(Ints, OS); + // Emit the intrinsic ID -> name table. EmitIntrinsicToNameTable(Ints, OS); // Emit the intrinsic ID -> overload table. EmitIntrinsicToOverloadTable(Ints, OS); - // Emit the function name recognizer. - EmitFnNameRecognizer(Ints, OS); - // Emit the intrinsic declaration generator. EmitGenerator(Ints, OS); // Emit the intrinsic parameter attributes. EmitAttributes(Ints, OS); - // Emit code to translate GCC builtins into LLVM intrinsics. - EmitIntrinsicToGCCBuiltinMap(Ints, OS); + // Individual targets don't need GCC builtin name mappings. + if (!TargetOnly) { + // Emit code to translate GCC builtins into LLVM intrinsics. + EmitIntrinsicToBuiltinMap(Ints, true, OS); - // Emit code to translate MS builtins into LLVM intrinsics. - EmitIntrinsicToMSBuiltinMap(Ints, OS); + // Emit code to translate MS builtins into LLVM intrinsics. + EmitIntrinsicToBuiltinMap(Ints, false, OS); + } EmitSuffix(OS); } @@ -117,7 +114,7 @@ void IntrinsicEmitter::EmitSuffix(raw_ostream &OS) { "#endif\n\n"; } -void IntrinsicEmitter::EmitEnumInfo(const std::vector<CodeGenIntrinsic> &Ints, +void IntrinsicEmitter::EmitEnumInfo(const CodeGenIntrinsicTable &Ints, raw_ostream &OS) { OS << "// Enum values for Intrinsics.h\n"; OS << "#ifdef GET_INTRINSIC_ENUM_VALUES\n"; @@ -131,64 +128,25 @@ void IntrinsicEmitter::EmitEnumInfo(const std::vector<CodeGenIntrinsic> &Ints, OS << "#endif\n\n"; } -void IntrinsicEmitter:: -EmitFnNameRecognizer(const std::vector<CodeGenIntrinsic> &Ints, - raw_ostream &OS) { - // Build a 'first character of function name' -> intrinsic # mapping. - std::map<char, std::vector<unsigned> > IntMapping; - for (unsigned i = 0, e = Ints.size(); i != e; ++i) - IntMapping[Ints[i].Name[5]].push_back(i); - - OS << "// Function name -> enum value recognizer code.\n"; - OS << "#ifdef GET_FUNCTION_RECOGNIZER\n"; - OS << " StringRef NameR(Name+6, Len-6); // Skip over 'llvm.'\n"; - OS << " switch (Name[5]) { // Dispatch on first letter.\n"; - OS << " default: break;\n"; - // Emit the intrinsic matching stuff by first letter. - for (std::map<char, std::vector<unsigned> >::iterator I = IntMapping.begin(), - E = IntMapping.end(); I != E; ++I) { - OS << " case '" << I->first << "':\n"; - std::vector<unsigned> &IntList = I->second; - - // Sort in reverse order of intrinsic name so "abc.def" appears after - // "abd.def.ghi" in the overridden name matcher - std::sort(IntList.begin(), IntList.end(), [&](unsigned i, unsigned j) { - return Ints[i].Name > Ints[j].Name; - }); - - // Emit all the overloaded intrinsics first, build a table of the - // non-overloaded ones. - std::vector<StringMatcher::StringPair> MatchTable; - - for (unsigned i = 0, e = IntList.size(); i != e; ++i) { - unsigned IntNo = IntList[i]; - std::string Result = "return " + TargetPrefix + "Intrinsic::" + - Ints[IntNo].EnumName + ";"; - - if (!Ints[IntNo].isOverloaded) { - MatchTable.push_back(std::make_pair(Ints[IntNo].Name.substr(6),Result)); - continue; - } - - // For overloaded intrinsics, only the prefix needs to match - std::string TheStr = Ints[IntNo].Name.substr(6); - TheStr += '.'; // Require "bswap." instead of bswap. - OS << " if (NameR.startswith(\"" << TheStr << "\")) " - << Result << '\n'; - } - - // Emit the matcher logic for the fixed length strings. - StringMatcher("NameR", MatchTable, OS).Emit(1); - OS << " break; // end of '" << I->first << "' case.\n"; - } - - OS << " }\n"; +void IntrinsicEmitter::EmitTargetInfo(const CodeGenIntrinsicTable &Ints, + raw_ostream &OS) { + OS << "// Target mapping\n"; + OS << "#ifdef GET_INTRINSIC_TARGET_DATA\n"; + OS << "struct IntrinsicTargetInfo {\n" + << " StringRef Name;\n" + << " size_t Offset;\n" + << " size_t Count;\n" + << "};\n"; + OS << "static const IntrinsicTargetInfo TargetInfos[] = {\n"; + for (auto Target : Ints.Targets) + OS << " {\"" << Target.Name << "\", " << Target.Offset << ", " + << Target.Count << "},\n"; + OS << "};\n"; OS << "#endif\n\n"; } -void IntrinsicEmitter:: -EmitIntrinsicToNameTable(const std::vector<CodeGenIntrinsic> &Ints, - raw_ostream &OS) { +void IntrinsicEmitter::EmitIntrinsicToNameTable( + const CodeGenIntrinsicTable &Ints, raw_ostream &OS) { OS << "// Intrinsic ID to name table\n"; OS << "#ifdef GET_INTRINSIC_NAME_TABLE\n"; OS << " // Note that entry #0 is the invalid intrinsic!\n"; @@ -197,9 +155,8 @@ EmitIntrinsicToNameTable(const std::vector<CodeGenIntrinsic> &Ints, OS << "#endif\n\n"; } -void IntrinsicEmitter:: -EmitIntrinsicToOverloadTable(const std::vector<CodeGenIntrinsic> &Ints, - raw_ostream &OS) { +void IntrinsicEmitter::EmitIntrinsicToOverloadTable( + const CodeGenIntrinsicTable &Ints, raw_ostream &OS) { OS << "// Intrinsic ID to overload bitset\n"; OS << "#ifdef GET_INTRINSIC_OVERLOAD_TABLE\n"; OS << "static const uint8_t OTable[] = {\n"; @@ -421,7 +378,7 @@ static void printIITEntry(raw_ostream &OS, unsigned char X) { OS << (unsigned)X; } -void IntrinsicEmitter::EmitGenerator(const std::vector<CodeGenIntrinsic> &Ints, +void IntrinsicEmitter::EmitGenerator(const CodeGenIntrinsicTable &Ints, raw_ostream &OS) { // If we can compute a 32-bit fixed encoding for this intrinsic, do so and // capture it in this vector, otherwise store a ~0U. @@ -520,8 +477,8 @@ struct AttributeComparator { return R->isConvergent; // Try to order by readonly/readnone attribute. - CodeGenIntrinsic::ModRefKind LK = L->ModRef; - CodeGenIntrinsic::ModRefKind RK = R->ModRef; + CodeGenIntrinsic::ModRefBehavior LK = L->ModRef; + CodeGenIntrinsic::ModRefBehavior RK = R->ModRef; if (LK != RK) return (LK > RK); // Order by argument attributes. @@ -532,8 +489,8 @@ struct AttributeComparator { } // End anonymous namespace /// EmitAttributes - This emits the Intrinsic::getAttributes method. -void IntrinsicEmitter:: -EmitAttributes(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS) { +void IntrinsicEmitter::EmitAttributes(const CodeGenIntrinsicTable &Ints, + raw_ostream &OS) { OS << "// Add parameter attributes that are not common to all intrinsics.\n"; OS << "#ifdef GET_INTRINSIC_ATTRIBUTES\n"; if (TargetOnly) @@ -606,12 +563,24 @@ EmitAttributes(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS) { OS << "Attribute::NoCapture"; addComma = true; break; + case CodeGenIntrinsic::Returned: + if (addComma) + OS << ","; + OS << "Attribute::Returned"; + addComma = true; + break; case CodeGenIntrinsic::ReadOnly: if (addComma) OS << ","; OS << "Attribute::ReadOnly"; addComma = true; break; + case CodeGenIntrinsic::WriteOnly: + if (addComma) + OS << ","; + OS << "Attribute::WriteOnly"; + addComma = true; + break; case CodeGenIntrinsic::ReadNone: if (addComma) OS << ","; @@ -674,6 +643,17 @@ EmitAttributes(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS) { OS << ","; OS << "Attribute::ReadOnly"; break; + case CodeGenIntrinsic::WriteArgMem: + if (addComma) + OS << ","; + OS << "Attribute::WriteOnly,"; + OS << "Attribute::ArgMemOnly"; + break; + case CodeGenIntrinsic::WriteMem: + if (addComma) + OS << ","; + OS << "Attribute::WriteOnly"; + break; case CodeGenIntrinsic::ReadWriteArgMem: if (addComma) OS << ","; @@ -704,56 +684,57 @@ EmitAttributes(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS) { OS << "#endif // GET_INTRINSIC_ATTRIBUTES\n\n"; } -/// EmitTargetBuiltins - All of the builtins in the specified map are for the -/// same target, and we already checked it. -static void EmitTargetBuiltins(const std::map<std::string, std::string> &BIM, - const std::string &TargetPrefix, - raw_ostream &OS) { - - std::vector<StringMatcher::StringPair> Results; - - for (std::map<std::string, std::string>::const_iterator I = BIM.begin(), - E = BIM.end(); I != E; ++I) { - std::string ResultCode = - "return " + TargetPrefix + "Intrinsic::" + I->second + ";"; - Results.emplace_back(I->first, ResultCode); - } - - StringMatcher("BuiltinName", Results, OS).Emit(); -} - - -void IntrinsicEmitter:: -EmitIntrinsicToGCCBuiltinMap(const std::vector<CodeGenIntrinsic> &Ints, - raw_ostream &OS) { - typedef std::map<std::string, std::map<std::string, std::string> > BIMTy; +void IntrinsicEmitter::EmitIntrinsicToBuiltinMap( + const CodeGenIntrinsicTable &Ints, bool IsGCC, raw_ostream &OS) { + StringRef CompilerName = (IsGCC ? "GCC" : "MS"); + typedef std::map<std::string, std::map<std::string, std::string>> BIMTy; BIMTy BuiltinMap; + StringToOffsetTable Table; for (unsigned i = 0, e = Ints.size(); i != e; ++i) { - if (!Ints[i].GCCBuiltinName.empty()) { + const std::string &BuiltinName = + IsGCC ? Ints[i].GCCBuiltinName : Ints[i].MSBuiltinName; + if (!BuiltinName.empty()) { // Get the map for this target prefix. - std::map<std::string, std::string> &BIM =BuiltinMap[Ints[i].TargetPrefix]; + std::map<std::string, std::string> &BIM = + BuiltinMap[Ints[i].TargetPrefix]; - if (!BIM.insert(std::make_pair(Ints[i].GCCBuiltinName, - Ints[i].EnumName)).second) + if (!BIM.insert(std::make_pair(BuiltinName, Ints[i].EnumName)).second) PrintFatalError("Intrinsic '" + Ints[i].TheDef->getName() + - "': duplicate GCC builtin name!"); + "': duplicate " + CompilerName + " builtin name!"); + Table.GetOrAddStringOffset(BuiltinName); } } - OS << "// Get the LLVM intrinsic that corresponds to a GCC builtin.\n"; - OS << "// This is used by the C front-end. The GCC builtin name is passed\n"; + OS << "// Get the LLVM intrinsic that corresponds to a builtin.\n"; + OS << "// This is used by the C front-end. The builtin name is passed\n"; OS << "// in as BuiltinName, and a target prefix (e.g. 'ppc') is passed\n"; OS << "// in as TargetPrefix. The result is assigned to 'IntrinsicID'.\n"; - OS << "#ifdef GET_LLVM_INTRINSIC_FOR_GCC_BUILTIN\n"; + OS << "#ifdef GET_LLVM_INTRINSIC_FOR_" << CompilerName << "_BUILTIN\n"; if (TargetOnly) { OS << "static " << TargetPrefix << "Intrinsic::ID " - << "getIntrinsicForGCCBuiltin(const char " + << "getIntrinsicFor" << CompilerName << "Builtin(const char " << "*TargetPrefixStr, const char *BuiltinNameStr) {\n"; } else { - OS << "Intrinsic::ID Intrinsic::getIntrinsicForGCCBuiltin(const char " + OS << "Intrinsic::ID Intrinsic::getIntrinsicFor" << CompilerName + << "Builtin(const char " << "*TargetPrefixStr, const char *BuiltinNameStr) {\n"; } + OS << " static const char BuiltinNames[] = {\n"; + Table.EmitCharArray(OS); + OS << " };\n\n"; + + OS << " struct BuiltinEntry {\n"; + OS << " Intrinsic::ID IntrinID;\n"; + OS << " unsigned StrTabOffset;\n"; + OS << " const char *getName() const {\n"; + OS << " return &BuiltinNames[StrTabOffset];\n"; + OS << " }\n"; + OS << " bool operator<(const char *RHS) const {\n"; + OS << " return strcmp(getName(), RHS) < 0;\n"; + OS << " }\n"; + OS << " };\n"; + OS << " StringRef BuiltinName(BuiltinNameStr);\n"; OS << " StringRef TargetPrefix(TargetPrefixStr);\n\n"; @@ -768,7 +749,18 @@ EmitIntrinsicToGCCBuiltinMap(const std::vector<CodeGenIntrinsic> &Ints, OS << "{\n"; // Emit the comparisons for this target prefix. - EmitTargetBuiltins(I->second, TargetPrefix, OS); + OS << " static const BuiltinEntry " << I->first << "Names[] = {\n"; + for (const auto &P : I->second) { + OS << " {Intrinsic::" << P.second << ", " + << Table.GetOrAddStringOffset(P.first) << "}, // " << P.first << "\n"; + } + OS << " };\n"; + OS << " auto I = std::lower_bound(std::begin(" << I->first << "Names),\n"; + OS << " std::end(" << I->first << "Names),\n"; + OS << " BuiltinNameStr);\n"; + OS << " if (I != std::end(" << I->first << "Names) &&\n"; + OS << " strcmp(I->getName(), BuiltinNameStr) == 0)\n"; + OS << " return I->IntrinID;\n"; OS << " }\n"; } OS << " return "; @@ -779,55 +771,6 @@ EmitIntrinsicToGCCBuiltinMap(const std::vector<CodeGenIntrinsic> &Ints, OS << "#endif\n\n"; } -void IntrinsicEmitter:: -EmitIntrinsicToMSBuiltinMap(const std::vector<CodeGenIntrinsic> &Ints, - raw_ostream &OS) { - std::map<std::string, std::map<std::string, std::string>> TargetBuiltins; - - for (const auto &Intrinsic : Ints) { - if (Intrinsic.MSBuiltinName.empty()) - continue; - - auto &Builtins = TargetBuiltins[Intrinsic.TargetPrefix]; - if (!Builtins.insert(std::make_pair(Intrinsic.MSBuiltinName, - Intrinsic.EnumName)).second) - PrintFatalError("Intrinsic '" + Intrinsic.TheDef->getName() + "': " - "duplicate MS builtin name!"); - } - - OS << "// Get the LLVM intrinsic that corresponds to a MS builtin.\n" - "// This is used by the C front-end. The MS builtin name is passed\n" - "// in as a BuiltinName, and a target prefix (e.g. 'arm') is passed\n" - "// in as a TargetPrefix. The result is assigned to 'IntrinsicID'.\n" - "#ifdef GET_LLVM_INTRINSIC_FOR_MS_BUILTIN\n"; - - OS << (TargetOnly ? "static " + TargetPrefix : "") << "Intrinsic::ID " - << (TargetOnly ? "" : "Intrinsic::") - << "getIntrinsicForMSBuiltin(const char *TP, const char *BN) {\n"; - OS << " StringRef BuiltinName(BN);\n" - " StringRef TargetPrefix(TP);\n" - "\n"; - - for (const auto &Builtins : TargetBuiltins) { - OS << " "; - if (Builtins.first.empty()) - OS << "/* Target Independent Builtins */ "; - else - OS << "if (TargetPrefix == \"" << Builtins.first << "\") "; - OS << "{\n"; - EmitTargetBuiltins(Builtins.second, TargetPrefix, OS); - OS << "}"; - } - - OS << " return "; - if (!TargetPrefix.empty()) - OS << "(" << TargetPrefix << "Intrinsic::ID)"; - OS << "Intrinsic::not_intrinsic;\n"; - OS << "}\n"; - - OS << "#endif\n\n"; -} - void llvm::EmitIntrinsics(RecordKeeper &RK, raw_ostream &OS, bool TargetOnly) { IntrinsicEmitter(RK, TargetOnly).run(OS); } diff --git a/utils/TableGen/Makefile b/utils/TableGen/Makefile deleted file mode 100644 index 9bfd94b7576bd..0000000000000 --- a/utils/TableGen/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -##===- utils/TableGen/Makefile -----------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../.. -TOOLNAME = llvm-tblgen -USEDLIBS = LLVMTableGen.a LLVMSupport.a - -# This tool has no plugins, optimize startup time. -TOOL_NO_EXPORTS = 1 - -include $(LEVEL)/Makefile.common - diff --git a/utils/TableGen/RegisterInfoEmitter.cpp b/utils/TableGen/RegisterInfoEmitter.cpp index b727df75626f2..9bb988f30f18a 100644 --- a/utils/TableGen/RegisterInfoEmitter.cpp +++ b/utils/TableGen/RegisterInfoEmitter.cpp @@ -16,22 +16,38 @@ #include "CodeGenRegisters.h" #include "CodeGenTarget.h" #include "SequenceToOffsetTable.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/BitVector.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/SparseBitVector.h" #include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Twine.h" +#include "llvm/CodeGen/MachineValueType.h" +#include "llvm/Support/Casting.h" #include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" #include "llvm/TableGen/Error.h" #include "llvm/TableGen/Record.h" +#include "llvm/TableGen/SetTheory.h" #include "llvm/TableGen/TableGenBackend.h" #include <algorithm> +#include <cassert> +#include <cstddef> +#include <cstdint> +#include <deque> +#include <iterator> #include <set> +#include <string> #include <vector> + using namespace llvm; namespace { + class RegisterInfoEmitter { RecordKeeper &Records; + public: RegisterInfoEmitter(RecordKeeper &R) : Records(R) {} @@ -65,7 +81,8 @@ private: void emitComposeSubRegIndexLaneMask(raw_ostream &OS, CodeGenRegBank &RegBank, const std::string &ClassName); }; -} // End anonymous namespace + +} // end anonymous namespace // runEnums - Print out enum values for all of the registers. void RegisterInfoEmitter::runEnums(raw_ostream &OS, @@ -81,7 +98,7 @@ void RegisterInfoEmitter::runEnums(raw_ostream &OS, emitSourceFileHeader("Target Register Enum Values", OS); OS << "\n#ifdef GET_REGINFO_ENUM\n"; - OS << "#undef GET_REGINFO_ENUM\n"; + OS << "#undef GET_REGINFO_ENUM\n\n"; OS << "namespace llvm {\n\n"; @@ -100,7 +117,7 @@ void RegisterInfoEmitter::runEnums(raw_ostream &OS, OS << " NUM_TARGET_REGS \t// " << Registers.size()+1 << "\n"; OS << "};\n"; if (!Namespace.empty()) - OS << "}\n"; + OS << "} // end namespace " << Namespace << "\n"; const auto &RegisterClasses = Bank.getRegClasses(); if (!RegisterClasses.empty()) { @@ -109,7 +126,7 @@ void RegisterInfoEmitter::runEnums(raw_ostream &OS, assert(RegisterClasses.size() <= 0xffff && "Too many register classes to fit in tables"); - OS << "\n// Register classes\n"; + OS << "\n// Register classes\n\n"; if (!Namespace.empty()) OS << "namespace " << Namespace << " {\n"; OS << "enum {\n"; @@ -118,14 +135,14 @@ void RegisterInfoEmitter::runEnums(raw_ostream &OS, << " = " << RC.EnumValue << ",\n"; OS << "\n };\n"; if (!Namespace.empty()) - OS << "}\n"; + OS << "} // end namespace " << Namespace << "\n\n"; } const std::vector<Record*> &RegAltNameIndices = Target.getRegAltNameIndices(); // If the only definition is the default NoRegAltName, we don't need to // emit anything. if (RegAltNameIndices.size() > 1) { - OS << "\n// Register alternate name indices\n"; + OS << "\n// Register alternate name indices\n\n"; if (!Namespace.empty()) OS << "namespace " << Namespace << " {\n"; OS << "enum {\n"; @@ -134,12 +151,12 @@ void RegisterInfoEmitter::runEnums(raw_ostream &OS, OS << " NUM_TARGET_REG_ALT_NAMES = " << RegAltNameIndices.size() << "\n"; OS << "};\n"; if (!Namespace.empty()) - OS << "}\n"; + OS << "} // end namespace " << Namespace << "\n\n"; } auto &SubRegIndices = Bank.getSubRegIndices(); if (!SubRegIndices.empty()) { - OS << "\n// Subregister indices\n"; + OS << "\n// Subregister indices\n\n"; std::string Namespace = SubRegIndices.front().getNamespace(); if (!Namespace.empty()) OS << "namespace " << Namespace << " {\n"; @@ -149,10 +166,10 @@ void RegisterInfoEmitter::runEnums(raw_ostream &OS, OS << " " << Idx.getName() << ",\t// " << ++i << "\n"; OS << " NUM_TARGET_SUBREGS\n};\n"; if (!Namespace.empty()) - OS << "}\n"; + OS << "} // end namespace " << Namespace << "\n\n"; } - OS << "} // End llvm namespace\n"; + OS << "} // end namespace llvm\n\n"; OS << "#endif // GET_REGINFO_ENUM\n\n"; } @@ -728,15 +745,11 @@ RegisterInfoEmitter::emitComposeSubRegIndexLaneMask(raw_ostream &OS, SubReg2SequenceIndexMap.push_back(Found); } - OS << "unsigned " << ClName - << "::composeSubRegIndexLaneMaskImpl(unsigned IdxA, unsigned LaneMask)" - " const {\n"; - OS << " struct MaskRolOp {\n" " unsigned Mask;\n" " uint8_t RotateLeft;\n" " };\n" - " static const MaskRolOp Seqs[] = {\n"; + " static const MaskRolOp LaneMaskComposeSequences[] = {\n"; unsigned Idx = 0; for (size_t s = 0, se = Sequences.size(); s != se; ++s) { OS << " "; @@ -756,24 +769,43 @@ RegisterInfoEmitter::emitComposeSubRegIndexLaneMask(raw_ostream &OS, for (size_t i = 0, e = SubRegIndices.size(); i != e; ++i) { OS << " "; unsigned Idx = SubReg2SequenceIndexMap[i]; - OS << format("&Seqs[%u]", Idx); + OS << format("&LaneMaskComposeSequences[%u]", Idx); if (i+1 != e) OS << ","; OS << " // to " << SubRegIndices[i].getName() << "\n"; } OS << " };\n\n"; - OS << " --IdxA; assert(IdxA < " << SubRegIndices.size() + OS << "LaneBitmask " << ClName + << "::composeSubRegIndexLaneMaskImpl(unsigned IdxA, LaneBitmask LaneMask)" + " const {\n" + " --IdxA; assert(IdxA < " << SubRegIndices.size() << " && \"Subregister index out of bounds\");\n" - " unsigned Result = 0;\n" + " LaneBitmask Result = 0;\n" " for (const MaskRolOp *Ops = CompositeSequences[IdxA]; Ops->Mask != 0; ++Ops)" " {\n" - " unsigned Masked = LaneMask & Ops->Mask;\n" + " LaneBitmask Masked = LaneMask & Ops->Mask;\n" " Result |= (Masked << Ops->RotateLeft) & 0xFFFFFFFF;\n" " Result |= (Masked >> ((32 - Ops->RotateLeft) & 0x1F));\n" " }\n" " return Result;\n" - "}\n"; + "}\n\n"; + + OS << "LaneBitmask " << ClName + << "::reverseComposeSubRegIndexLaneMaskImpl(unsigned IdxA, " + " LaneBitmask LaneMask) const {\n" + " LaneMask &= getSubRegIndexLaneMask(IdxA);\n" + " --IdxA; assert(IdxA < " << SubRegIndices.size() + << " && \"Subregister index out of bounds\");\n" + " LaneBitmask Result = 0;\n" + " for (const MaskRolOp *Ops = CompositeSequences[IdxA]; Ops->Mask != 0; ++Ops)" + " {\n" + " LaneBitmask Rotated = (LaneMask >> Ops->RotateLeft) |\n" + " ((LaneMask << ((32 - Ops->RotateLeft) & 0x1F)) & 0xFFFFFFFF);\n" + " Result |= Rotated & Ops->Mask;\n" + " }\n" + " return Result;\n" + "}\n\n"; } // @@ -785,7 +817,7 @@ RegisterInfoEmitter::runMCDesc(raw_ostream &OS, CodeGenTarget &Target, emitSourceFileHeader("MC Register Information", OS); OS << "\n#ifdef GET_REGINFO_MC_DESC\n"; - OS << "#undef GET_REGINFO_MC_DESC\n"; + OS << "#undef GET_REGINFO_MC_DESC\n\n"; const auto &Regs = RegBank.getRegisters(); @@ -958,7 +990,7 @@ RegisterInfoEmitter::runMCDesc(raw_ostream &OS, CodeGenTarget &Target, ArrayRef<Record*> Order = RC.getOrder(); // Give the register class a legal C name if it's anonymous. - std::string Name = RC.getName(); + const std::string &Name = RC.getName(); RegClassStrings.add(Name); @@ -984,7 +1016,7 @@ RegisterInfoEmitter::runMCDesc(raw_ostream &OS, CodeGenTarget &Target, OS << "\n };\n\n"; } - OS << "}\n\n"; + OS << "} // end anonymous namespace\n\n"; RegClassStrings.layout(); OS << "extern const char " << TargetName << "RegClassStrings[] = {\n"; @@ -1008,7 +1040,7 @@ RegisterInfoEmitter::runMCDesc(raw_ostream &OS, CodeGenTarget &Target, << RC.SpillSize/8 << ", " << RC.SpillAlignment/8 << ", " << RC.CopyCost << ", " - << RC.Allocatable << " },\n"; + << ( RC.Allocatable ? "true" : "false" ) << " },\n"; } OS << "};\n\n"; @@ -1051,7 +1083,7 @@ RegisterInfoEmitter::runMCDesc(raw_ostream &OS, CodeGenTarget &Target, OS << "}\n\n"; - OS << "} // End llvm namespace\n"; + OS << "} // end namespace llvm\n\n"; OS << "#endif // GET_REGINFO_MC_DESC\n\n"; } @@ -1061,7 +1093,7 @@ RegisterInfoEmitter::runTargetHeader(raw_ostream &OS, CodeGenTarget &Target, emitSourceFileHeader("Register Information Header Fragment", OS); OS << "\n#ifdef GET_REGINFO_HEADER\n"; - OS << "#undef GET_REGINFO_HEADER\n"; + OS << "#undef GET_REGINFO_HEADER\n\n"; const std::string &TargetName = Target.getName(); std::string ClassName = TargetName + "GenRegisterInfo"; @@ -1078,8 +1110,10 @@ RegisterInfoEmitter::runTargetHeader(raw_ostream &OS, CodeGenTarget &Target, if (!RegBank.getSubRegIndices().empty()) { OS << " unsigned composeSubRegIndicesImpl" << "(unsigned, unsigned) const override;\n" - << " unsigned composeSubRegIndexLaneMaskImpl" - << "(unsigned, unsigned) const override;\n" + << " LaneBitmask composeSubRegIndexLaneMaskImpl" + << "(unsigned, LaneBitmask) const override;\n" + << " LaneBitmask reverseComposeSubRegIndexLaneMaskImpl" + << "(unsigned, LaneBitmask) const override;\n" << " const TargetRegisterClass *getSubClassWithSubReg" << "(const TargetRegisterClass*, unsigned) const override;\n"; } @@ -1113,9 +1147,9 @@ RegisterInfoEmitter::runTargetHeader(raw_ostream &OS, CodeGenTarget &Target, // Output the extern for the instance. OS << " extern const TargetRegisterClass " << Name << "RegClass;\n"; } - OS << "} // end of namespace " << TargetName << "\n\n"; + OS << "} // end namespace " << RegisterClasses.front().Namespace << "\n\n"; } - OS << "} // End llvm namespace\n"; + OS << "} // end namespace llvm\n\n"; OS << "#endif // GET_REGINFO_HEADER\n\n"; } @@ -1128,7 +1162,7 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target, emitSourceFileHeader("Target Register and Register Classes Information", OS); OS << "\n#ifdef GET_REGINFO_TARGET_DESC\n"; - OS << "#undef GET_REGINFO_TARGET_DESC\n"; + OS << "#undef GET_REGINFO_TARGET_DESC\n\n"; OS << "namespace llvm {\n\n"; @@ -1294,7 +1328,9 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target, << format("0x%08x,\n ", RC.LaneMask) << (unsigned)RC.AllocationPriority << ",\n " << (RC.HasDisjunctSubRegs?"true":"false") - << ", /* HasDisjunctSubRegs */\n "; + << ", /* HasDisjunctSubRegs */\n " + << (RC.CoveredBySubRegs?"true":"false") + << ", /* CoveredBySubRegs */\n "; if (RC.getSuperClasses().empty()) OS << "NullRegClasses,\n "; else @@ -1306,7 +1342,7 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target, OS << " };\n\n"; } - OS << "}\n"; + OS << "} // end namespace " << RegisterClasses.front().Namespace << "\n"; } OS << "\nnamespace {\n"; @@ -1314,19 +1350,20 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target, for (const auto &RC : RegisterClasses) OS << " &" << RC.getQualifiedName() << "RegClass,\n"; OS << " };\n"; - OS << "}\n"; // End of anonymous namespace... + OS << "} // end anonymous namespace\n"; // Emit extra information about registers. const std::string &TargetName = Target.getName(); OS << "\nstatic const TargetRegisterInfoDesc " << TargetName << "RegInfoDesc[] = { // Extra Descriptors\n"; - OS << " { 0, 0 },\n"; + OS << " { 0, false },\n"; const auto &Regs = RegBank.getRegisters(); for (const auto &Reg : Regs) { OS << " { "; OS << Reg.CostPerUse << ", " - << int(AllocatableRegs.count(Reg.TheDef)) << " },\n"; + << ( AllocatableRegs.count(Reg.TheDef) != 0 ? "true" : "false" ) + << " },\n"; } OS << "};\n"; // End of register descriptors... @@ -1414,7 +1451,6 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target, OS << "}\n\n"; - // Emit CalleeSavedRegs information. std::vector<Record*> CSRSets = Records.getAllDerivedDefinitions("CalleeSavedRegs"); @@ -1482,7 +1518,7 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target, << " MF.getSubtarget().getFrameLowering());\n" << "}\n\n"; - OS << "} // End llvm namespace\n"; + OS << "} // end namespace llvm\n\n"; OS << "#endif // GET_REGINFO_TARGET_DESC\n\n"; } @@ -1503,4 +1539,4 @@ void EmitRegisterInfo(RecordKeeper &RK, raw_ostream &OS) { RegisterInfoEmitter(RK).run(OS); } -} // End llvm namespace +} // end namespace llvm diff --git a/utils/TableGen/SearchableTableEmitter.cpp b/utils/TableGen/SearchableTableEmitter.cpp new file mode 100644 index 0000000000000..8c1b8804d1741 --- /dev/null +++ b/utils/TableGen/SearchableTableEmitter.cpp @@ -0,0 +1,320 @@ +//===- SearchableTableEmitter.cpp - Generate efficiently searchable tables -==// +// +// 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 a generic array initialized by specified fields, +// together with companion index tables and lookup functions (binary search, +// currently). +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include <algorithm> +#include <sstream> +#include <string> +#include <vector> +using namespace llvm; + +#define DEBUG_TYPE "searchable-table-emitter" + +namespace { + +class SearchableTableEmitter { + RecordKeeper &Records; + +public: + SearchableTableEmitter(RecordKeeper &R) : Records(R) {} + + void run(raw_ostream &OS); + +private: + typedef std::pair<Init *, int> SearchTableEntry; + + int getAsInt(BitsInit *B) { + return cast<IntInit>(B->convertInitializerTo(IntRecTy::get()))->getValue(); + } + int getInt(Record *R, StringRef Field) { + return getAsInt(R->getValueAsBitsInit(Field)); + } + + std::string primaryRepresentation(Init *I) { + if (StringInit *SI = dyn_cast<StringInit>(I)) + return SI->getAsString(); + else if (BitsInit *BI = dyn_cast<BitsInit>(I)) + return "0x" + utohexstr(getAsInt(BI)); + else if (BitInit *BI = dyn_cast<BitInit>(I)) + return BI->getValue() ? "true" : "false"; + else if (CodeInit *CI = dyn_cast<CodeInit>(I)) { + return CI->getValue(); + } + PrintFatalError(SMLoc(), + "invalid field type, expected: string, bits, bit or code"); + } + + std::string searchRepresentation(Init *I) { + std::string PrimaryRep = primaryRepresentation(I); + if (!isa<StringInit>(I)) + return PrimaryRep; + return StringRef(PrimaryRep).upper(); + } + + std::string searchableFieldType(Init *I) { + if (isa<StringInit>(I)) + return "const char *"; + else if (BitsInit *BI = dyn_cast<BitsInit>(I)) { + unsigned NumBits = BI->getNumBits(); + if (NumBits <= 8) + NumBits = 8; + else if (NumBits <= 16) + NumBits = 16; + else if (NumBits <= 32) + NumBits = 32; + else if (NumBits <= 64) + NumBits = 64; + else + PrintFatalError(SMLoc(), "bitfield too large to search"); + return "uint" + utostr(NumBits) + "_t"; + } + PrintFatalError(SMLoc(), "Unknown type to search by"); + } + + void emitMapping(Record *MappingDesc, raw_ostream &OS); + void emitMappingEnum(std::vector<Record *> &Items, Record *InstanceClass, + raw_ostream &OS); + void + emitPrimaryTable(StringRef Name, std::vector<std::string> &FieldNames, + std::vector<std::string> &SearchFieldNames, + std::vector<std::vector<SearchTableEntry>> &SearchTables, + std::vector<Record *> &Items, raw_ostream &OS); + void emitSearchTable(StringRef Name, StringRef Field, + std::vector<SearchTableEntry> &SearchTable, + raw_ostream &OS); + void emitLookupDeclaration(StringRef Name, StringRef Field, Init *I, + raw_ostream &OS); + void emitLookupFunction(StringRef Name, StringRef Field, Init *I, + raw_ostream &OS); +}; + +} // End anonymous namespace. + +/// Emit an enum providing symbolic access to some preferred field from +/// C++. +void SearchableTableEmitter::emitMappingEnum(std::vector<Record *> &Items, + Record *InstanceClass, + raw_ostream &OS) { + std::string EnumNameField = InstanceClass->getValueAsString("EnumNameField"); + std::string EnumValueField; + if (!InstanceClass->isValueUnset("EnumValueField")) + EnumValueField = InstanceClass->getValueAsString("EnumValueField"); + + OS << "enum " << InstanceClass->getName() << "Values {\n"; + for (auto Item : Items) { + OS << " " << Item->getValueAsString(EnumNameField); + if (EnumValueField != StringRef()) + OS << " = " << getInt(Item, EnumValueField); + OS << ",\n"; + } + OS << "};\n\n"; +} + +void SearchableTableEmitter::emitPrimaryTable( + StringRef Name, std::vector<std::string> &FieldNames, + std::vector<std::string> &SearchFieldNames, + std::vector<std::vector<SearchTableEntry>> &SearchTables, + std::vector<Record *> &Items, raw_ostream &OS) { + OS << "const " << Name << " " << Name << "sList[] = {\n"; + + for (auto Item : Items) { + OS << " { "; + for (unsigned i = 0; i < FieldNames.size(); ++i) { + OS << primaryRepresentation(Item->getValueInit(FieldNames[i])); + if (i != FieldNames.size() - 1) + OS << ", "; + } + OS << "},\n"; + } + OS << "};\n\n"; +} + +void SearchableTableEmitter::emitSearchTable( + StringRef Name, StringRef Field, std::vector<SearchTableEntry> &SearchTable, + raw_ostream &OS) { + OS << "const std::pair<" << searchableFieldType(SearchTable[0].first) + << ", int> " << Name << "sBy" << Field << "[] = {\n"; + + if (isa<BitsInit>(SearchTable[0].first)) { + std::stable_sort(SearchTable.begin(), SearchTable.end(), + [this](const SearchTableEntry &LHS, + const SearchTableEntry &RHS) { + return getAsInt(cast<BitsInit>(LHS.first)) < + getAsInt(cast<BitsInit>(RHS.first)); + }); + } else { + std::stable_sort(SearchTable.begin(), SearchTable.end(), + [this](const SearchTableEntry &LHS, + const SearchTableEntry &RHS) { + return searchRepresentation(LHS.first) < + searchRepresentation(RHS.first); + }); + } + + for (auto Entry : SearchTable) { + OS << " { " << searchRepresentation(Entry.first) << ", " << Entry.second + << " },\n"; + } + OS << "};\n\n"; +} + +void SearchableTableEmitter::emitLookupFunction(StringRef Name, StringRef Field, + Init *I, raw_ostream &OS) { + bool IsIntegral = isa<BitsInit>(I); + std::string FieldType = searchableFieldType(I); + std::string PairType = "std::pair<" + FieldType + ", int>"; + + // const SysRegs *lookupSysRegByName(const char *Name) { + OS << "const " << Name << " *" + << "lookup" << Name << "By" << Field; + OS << "(" << (IsIntegral ? FieldType : "StringRef") << " " << Field + << ") {\n"; + + if (IsIntegral) { + OS << " auto CanonicalVal = " << Field << ";\n"; + OS << " " << PairType << " Val = {CanonicalVal, 0};\n"; + } else { + // Make sure the result is null terminated because it's going via "char *". + OS << " std::string CanonicalVal = " << Field << ".upper();\n"; + OS << " " << PairType << " Val = {CanonicalVal.data(), 0};\n"; + } + + OS << " ArrayRef<" << PairType << "> Table(" << Name << "sBy" << Field + << ");\n"; + OS << " auto Idx = std::lower_bound(Table.begin(), Table.end(), Val"; + + if (IsIntegral) + OS << ");\n"; + else { + OS << ",\n "; + OS << "[](const " << PairType << " &LHS, const " << PairType + << " &RHS) {\n"; + OS << " return StringRef(LHS.first) < StringRef(RHS.first);\n"; + OS << " });\n\n"; + } + + OS << " if (Idx == Table.end() || CanonicalVal != Idx->first)\n"; + OS << " return nullptr;\n"; + + OS << " return &" << Name << "sList[Idx->second];\n"; + OS << "}\n\n"; +} + +void SearchableTableEmitter::emitLookupDeclaration(StringRef Name, + StringRef Field, Init *I, + raw_ostream &OS) { + bool IsIntegral = isa<BitsInit>(I); + std::string FieldType = searchableFieldType(I); + OS << "const " << Name << " *" + << "lookup" << Name << "By" << Field; + OS << "(" << (IsIntegral ? FieldType : "StringRef") << " " << Field + << ");\n\n"; +} + +void SearchableTableEmitter::emitMapping(Record *InstanceClass, + raw_ostream &OS) { + const std::string &TableName = InstanceClass->getName(); + std::vector<Record *> Items = Records.getAllDerivedDefinitions(TableName); + + // Gather all the records we're going to need for this particular mapping. + std::vector<std::vector<SearchTableEntry>> SearchTables; + std::vector<std::string> SearchFieldNames; + + std::vector<std::string> FieldNames; + for (const RecordVal &Field : InstanceClass->getValues()) { + std::string FieldName = Field.getName(); + + // Skip uninteresting fields: either built-in, special to us, or injected + // template parameters (if they contain a ':'). + if (FieldName.find(':') != std::string::npos || FieldName == "NAME" || + FieldName == "SearchableFields" || FieldName == "EnumNameField" || + FieldName == "EnumValueField") + continue; + + FieldNames.push_back(FieldName); + } + + for (auto *Field : *InstanceClass->getValueAsListInit("SearchableFields")) { + SearchTables.emplace_back(); + SearchFieldNames.push_back(Field->getAsUnquotedString()); + } + + int Idx = 0; + for (Record *Item : Items) { + for (unsigned i = 0; i < SearchFieldNames.size(); ++i) { + Init *SearchVal = Item->getValueInit(SearchFieldNames[i]); + SearchTables[i].emplace_back(SearchVal, Idx); + } + ++Idx; + } + + OS << "#ifdef GET_" << StringRef(TableName).upper() << "_DECL\n"; + OS << "#undef GET_" << StringRef(TableName).upper() << "_DECL\n"; + + // Next emit the enum containing the top-level names for use in C++ code if + // requested + if (!InstanceClass->isValueUnset("EnumNameField")) { + emitMappingEnum(Items, InstanceClass, OS); + } + + // And the declarations for the functions that will perform lookup. + for (unsigned i = 0; i < SearchFieldNames.size(); ++i) + emitLookupDeclaration(TableName, SearchFieldNames[i], + SearchTables[i][0].first, OS); + + OS << "#endif\n\n"; + + OS << "#ifdef GET_" << StringRef(TableName).upper() << "_IMPL\n"; + OS << "#undef GET_" << StringRef(TableName).upper() << "_IMPL\n"; + + // The primary data table contains all the fields defined for this map. + emitPrimaryTable(TableName, FieldNames, SearchFieldNames, SearchTables, Items, + OS); + + // Indexes are sorted "{ Thing, PrimaryIdx }" arrays, so that a binary + // search can be performed by "Thing". + for (unsigned i = 0; i < SearchTables.size(); ++i) { + emitSearchTable(TableName, SearchFieldNames[i], SearchTables[i], OS); + emitLookupFunction(TableName, SearchFieldNames[i], SearchTables[i][0].first, + OS); + } + + OS << "#endif\n"; +} + +void SearchableTableEmitter::run(raw_ostream &OS) { + // Tables are defined to be the direct descendents of "SearchableEntry". + Record *SearchableTable = Records.getClass("SearchableTable"); + for (auto &NameRec : Records.getClasses()) { + Record *Class = NameRec.second.get(); + if (Class->getSuperClasses().size() != 1 || + !Class->isSubClassOf(SearchableTable)) + continue; + emitMapping(Class, OS); + } +} + +namespace llvm { + +void EmitSearchableTables(RecordKeeper &RK, raw_ostream &OS) { + SearchableTableEmitter(RK).run(OS); +} + +} // End llvm namespace. diff --git a/utils/TableGen/SequenceToOffsetTable.h b/utils/TableGen/SequenceToOffsetTable.h index 66506ea0638f0..e026b1c9fbf02 100644 --- a/utils/TableGen/SequenceToOffsetTable.h +++ b/utils/TableGen/SequenceToOffsetTable.h @@ -22,7 +22,6 @@ #include <cctype> #include <functional> #include <map> -#include <vector> namespace llvm { diff --git a/utils/TableGen/SubtargetEmitter.cpp b/utils/TableGen/SubtargetEmitter.cpp index d056de003e189..228882177bdf6 100644 --- a/utils/TableGen/SubtargetEmitter.cpp +++ b/utils/TableGen/SubtargetEmitter.cpp @@ -13,16 +13,20 @@ #include "CodeGenTarget.h" #include "CodeGenSchedule.h" -#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/StringExtras.h" #include "llvm/MC/MCInstrItineraries.h" +#include "llvm/MC/MCSchedule.h" #include "llvm/MC/SubtargetFeature.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" #include "llvm/TableGen/Error.h" #include "llvm/TableGen/Record.h" #include "llvm/TableGen/TableGenBackend.h" #include <algorithm> +#include <cassert> +#include <cstdint> #include <map> #include <string> #include <vector> @@ -32,6 +36,7 @@ using namespace llvm; #define DEBUG_TYPE "subtarget-emitter" namespace { + class SubtargetEmitter { // Each processor has a SchedClassDesc table with an entry for each SchedClass. // The SchedClassDesc table indexes into a global write resource table, write @@ -64,7 +69,7 @@ class SubtargetEmitter { CodeGenSchedModels &SchedModels; std::string Target; - void Enumeration(raw_ostream &OS, const char *ClassName); + void Enumeration(raw_ostream &OS); unsigned FeatureKeyValues(raw_ostream &OS); unsigned CPUKeyValues(raw_ostream &OS); void FormItineraryStageString(const std::string &Names, @@ -96,7 +101,7 @@ class SubtargetEmitter { void EmitSchedClassTables(SchedClassTables &SchedTables, raw_ostream &OS); void EmitProcessorModels(raw_ostream &OS); void EmitProcessorLookup(raw_ostream &OS); - void EmitSchedModelHelpers(std::string ClassName, raw_ostream &OS); + void EmitSchedModelHelpers(const std::string &ClassName, raw_ostream &OS); void EmitSchedModel(raw_ostream &OS); void ParseFeaturesFunction(raw_ostream &OS, unsigned NumFeatures, unsigned NumProcs); @@ -107,15 +112,16 @@ public: void run(raw_ostream &o); }; + } // end anonymous namespace // // Enumeration - Emit the specified class as an enumeration. // -void SubtargetEmitter::Enumeration(raw_ostream &OS, - const char *ClassName) { +void SubtargetEmitter::Enumeration(raw_ostream &OS) { // Get all records of class and sort - std::vector<Record*> DefList = Records.getAllDerivedDefinitions(ClassName); + std::vector<Record*> DefList = + Records.getAllDerivedDefinitions("SubtargetFeature"); std::sort(DefList.begin(), DefList.end(), LessRecord()); unsigned N = DefList.size(); @@ -126,8 +132,8 @@ void SubtargetEmitter::Enumeration(raw_ostream &OS, OS << "namespace " << Target << " {\n"; - // Open enumeration. Use a 64-bit underlying type. - OS << "enum : uint64_t {\n"; + // Open enumeration. + OS << "enum {\n"; // For each record for (unsigned i = 0; i < N;) { @@ -142,7 +148,8 @@ void SubtargetEmitter::Enumeration(raw_ostream &OS, } // Close enumeration and namespace - OS << "};\n}\n"; + OS << "};\n"; + OS << "} // end namespace " << Target << "\n"; } // @@ -357,17 +364,16 @@ EmitStageAndOperandCycleData(raw_ostream &OS, SmallPtrSet<Record*, 8> ItinsDefSet; // Emit functional units for all the itineraries. - for (CodeGenSchedModels::ProcIter PI = SchedModels.procModelBegin(), - PE = SchedModels.procModelEnd(); PI != PE; ++PI) { + for (const CodeGenProcModel &ProcModel : SchedModels.procModels()) { - if (!ItinsDefSet.insert(PI->ItinsDef).second) + if (!ItinsDefSet.insert(ProcModel.ItinsDef).second) continue; - std::vector<Record*> FUs = PI->ItinsDef->getValueAsListOfDefs("FU"); + std::vector<Record*> FUs = ProcModel.ItinsDef->getValueAsListOfDefs("FU"); if (FUs.empty()) continue; - const std::string &Name = PI->ItinsDef->getName(); + const std::string &Name = ProcModel.ItinsDef->getName(); OS << "\n// Functional units for \"" << Name << "\"\n" << "namespace " << Name << "FU {\n"; @@ -375,9 +381,9 @@ EmitStageAndOperandCycleData(raw_ostream &OS, OS << " const unsigned " << FUs[j]->getName() << " = 1 << " << j << ";\n"; - OS << "}\n"; + OS << "} // end namespace " << Name << "FU\n"; - std::vector<Record*> BPs = PI->ItinsDef->getValueAsListOfDefs("BP"); + std::vector<Record*> BPs = ProcModel.ItinsDef->getValueAsListOfDefs("BP"); if (!BPs.empty()) { OS << "\n// Pipeline forwarding pathes for itineraries \"" << Name << "\"\n" << "namespace " << Name << "Bypass {\n"; @@ -387,7 +393,7 @@ EmitStageAndOperandCycleData(raw_ostream &OS, OS << " const unsigned " << BPs[j]->getName() << " = 1 << " << j << ";\n"; - OS << "}\n"; + OS << "} // end namespace " << Name << "Bypass\n"; } } @@ -411,10 +417,7 @@ EmitStageAndOperandCycleData(raw_ostream &OS, // object with computed offsets to the ProcItinLists result. unsigned StageCount = 1, OperandCycleCount = 1; std::map<std::string, unsigned> ItinStageMap, ItinOperandMap; - for (CodeGenSchedModels::ProcIter PI = SchedModels.procModelBegin(), - PE = SchedModels.procModelEnd(); PI != PE; ++PI) { - const CodeGenProcModel &ProcModel = *PI; - + for (const CodeGenProcModel &ProcModel : SchedModels.procModels()) { // Add process itinerary to the list. ProcItinLists.resize(ProcItinLists.size()+1); @@ -612,9 +615,8 @@ void SubtargetEmitter::EmitProcessorResources(const CodeGenProcModel &ProcModel, int BufferSize = PRDef->getValueAsInt("BufferSize"); if (PRDef->isSubClassOf("ProcResGroup")) { RecVec ResUnits = PRDef->getValueAsListOfDefs("Resources"); - for (RecIter RUI = ResUnits.begin(), RUE = ResUnits.end(); - RUI != RUE; ++RUI) { - NumUnits += (*RUI)->getValueAsInt("NumUnits"); + for (Record *RU : ResUnits) { + NumUnits += RU->getValueAsInt("NumUnits"); } } else { @@ -652,10 +654,9 @@ Record *SubtargetEmitter::FindWriteResources( return SchedWrite.TheDef; Record *AliasDef = nullptr; - for (RecIter AI = SchedWrite.Aliases.begin(), AE = SchedWrite.Aliases.end(); - AI != AE; ++AI) { + for (Record *A : SchedWrite.Aliases) { const CodeGenSchedRW &AliasRW = - SchedModels.getSchedRW((*AI)->getValueAsDef("AliasRW")); + SchedModels.getSchedRW(A->getValueAsDef("AliasRW")); if (AliasRW.TheDef->getValueInit("SchedModel")->isComplete()) { Record *ModelDef = AliasRW.TheDef->getValueAsDef("SchedModel"); if (&SchedModels.getProcModel(ModelDef) != &ProcModel) @@ -672,18 +673,17 @@ Record *SubtargetEmitter::FindWriteResources( // Check this processor's list of write resources. Record *ResDef = nullptr; - for (RecIter WRI = ProcModel.WriteResDefs.begin(), - WRE = ProcModel.WriteResDefs.end(); WRI != WRE; ++WRI) { - if (!(*WRI)->isSubClassOf("WriteRes")) + for (Record *WR : ProcModel.WriteResDefs) { + if (!WR->isSubClassOf("WriteRes")) continue; - if (AliasDef == (*WRI)->getValueAsDef("WriteType") - || SchedWrite.TheDef == (*WRI)->getValueAsDef("WriteType")) { + if (AliasDef == WR->getValueAsDef("WriteType") + || SchedWrite.TheDef == WR->getValueAsDef("WriteType")) { if (ResDef) { - PrintFatalError((*WRI)->getLoc(), "Resources are defined for both " + PrintFatalError(WR->getLoc(), "Resources are defined for both " "SchedWrite and its alias on processor " + ProcModel.ModelName); } - ResDef = *WRI; + ResDef = WR; } } // TODO: If ProcModel has a base model (previous generation processor), @@ -706,10 +706,9 @@ Record *SubtargetEmitter::FindReadAdvance(const CodeGenSchedRW &SchedRead, // Check this processor's list of aliases for SchedRead. Record *AliasDef = nullptr; - for (RecIter AI = SchedRead.Aliases.begin(), AE = SchedRead.Aliases.end(); - AI != AE; ++AI) { + for (Record *A : SchedRead.Aliases) { const CodeGenSchedRW &AliasRW = - SchedModels.getSchedRW((*AI)->getValueAsDef("AliasRW")); + SchedModels.getSchedRW(A->getValueAsDef("AliasRW")); if (AliasRW.TheDef->getValueInit("SchedModel")->isComplete()) { Record *ModelDef = AliasRW.TheDef->getValueAsDef("SchedModel"); if (&SchedModels.getProcModel(ModelDef) != &ProcModel) @@ -726,18 +725,17 @@ Record *SubtargetEmitter::FindReadAdvance(const CodeGenSchedRW &SchedRead, // Check this processor's ReadAdvanceList. Record *ResDef = nullptr; - for (RecIter RAI = ProcModel.ReadAdvanceDefs.begin(), - RAE = ProcModel.ReadAdvanceDefs.end(); RAI != RAE; ++RAI) { - if (!(*RAI)->isSubClassOf("ReadAdvance")) + for (Record *RA : ProcModel.ReadAdvanceDefs) { + if (!RA->isSubClassOf("ReadAdvance")) continue; - if (AliasDef == (*RAI)->getValueAsDef("ReadType") - || SchedRead.TheDef == (*RAI)->getValueAsDef("ReadType")) { + if (AliasDef == RA->getValueAsDef("ReadType") + || SchedRead.TheDef == RA->getValueAsDef("ReadType")) { if (ResDef) { - PrintFatalError((*RAI)->getLoc(), "Resources are defined for both " + PrintFatalError(RA->getLoc(), "Resources are defined for both " "SchedRead and its alias on processor " + ProcModel.ModelName); } - ResDef = *RAI; + ResDef = RA; } } // TODO: If ProcModel has a base model (previous generation processor), @@ -779,12 +777,10 @@ void SubtargetEmitter::ExpandProcResources(RecVec &PRVec, SubDef = SuperDef; } } - for (RecIter PRI = PM.ProcResourceDefs.begin(), - PRE = PM.ProcResourceDefs.end(); - PRI != PRE; ++PRI) { - if (*PRI == PRDef || !(*PRI)->isSubClassOf("ProcResGroup")) + for (Record *PR : PM.ProcResourceDefs) { + if (PR == PRDef || !PR->isSubClassOf("ProcResGroup")) continue; - RecVec SuperResources = (*PRI)->getValueAsListOfDefs("Resources"); + RecVec SuperResources = PR->getValueAsListOfDefs("Resources"); RecIter SubI = SubResources.begin(), SubE = SubResources.end(); for( ; SubI != SubE; ++SubI) { if (std::find(SuperResources.begin(), SuperResources.end(), *SubI) @@ -793,7 +789,7 @@ void SubtargetEmitter::ExpandProcResources(RecVec &PRVec, } } if (SubI == SubE) { - PRVec.push_back(*PRI); + PRVec.push_back(PR); Cycles.push_back(Cycles[i]); } } @@ -809,9 +805,8 @@ void SubtargetEmitter::GenSchedClassTables(const CodeGenProcModel &ProcModel, return; std::vector<MCSchedClassDesc> &SCTab = SchedTables.ProcSchedClasses.back(); - for (CodeGenSchedModels::SchedClassIter SCI = SchedModels.schedClassBegin(), - SCE = SchedModels.schedClassEnd(); SCI != SCE; ++SCI) { - DEBUG(SCI->dump(&SchedModels)); + for (const CodeGenSchedClass &SC : SchedModels.schedClasses()) { + DEBUG(SC.dump(&SchedModels)); SCTab.resize(SCTab.size() + 1); MCSchedClassDesc &SCDesc = SCTab.back(); @@ -826,7 +821,7 @@ void SubtargetEmitter::GenSchedClassTables(const CodeGenProcModel &ProcModel, // A Variant SchedClass has no resources of its own. bool HasVariants = false; for (std::vector<CodeGenSchedTransition>::const_iterator - TI = SCI->Transitions.begin(), TE = SCI->Transitions.end(); + TI = SC.Transitions.begin(), TE = SC.Transitions.end(); TI != TE; ++TI) { if (TI->ProcIndices[0] == 0) { HasVariants = true; @@ -847,24 +842,23 @@ void SubtargetEmitter::GenSchedClassTables(const CodeGenProcModel &ProcModel, // Determine if the SchedClass is actually reachable on this processor. If // not don't try to locate the processor resources, it will fail. // If ProcIndices contains 0, this class applies to all processors. - assert(!SCI->ProcIndices.empty() && "expect at least one procidx"); - if (SCI->ProcIndices[0] != 0) { - IdxIter PIPos = std::find(SCI->ProcIndices.begin(), - SCI->ProcIndices.end(), ProcModel.Index); - if (PIPos == SCI->ProcIndices.end()) + assert(!SC.ProcIndices.empty() && "expect at least one procidx"); + if (SC.ProcIndices[0] != 0) { + IdxIter PIPos = std::find(SC.ProcIndices.begin(), + SC.ProcIndices.end(), ProcModel.Index); + if (PIPos == SC.ProcIndices.end()) continue; } - IdxVec Writes = SCI->Writes; - IdxVec Reads = SCI->Reads; - if (!SCI->InstRWs.empty()) { + IdxVec Writes = SC.Writes; + IdxVec Reads = SC.Reads; + if (!SC.InstRWs.empty()) { // This class has a default ReadWrite list which can be overriden by // InstRW definitions. Record *RWDef = nullptr; - for (RecIter RWI = SCI->InstRWs.begin(), RWE = SCI->InstRWs.end(); - RWI != RWE; ++RWI) { - Record *RWModelDef = (*RWI)->getValueAsDef("SchedModel"); + for (Record *RW : SC.InstRWs) { + Record *RWModelDef = RW->getValueAsDef("SchedModel"); if (&ProcModel == &SchedModels.getProcModel(RWModelDef)) { - RWDef = *RWI; + RWDef = RW; break; } } @@ -877,19 +871,18 @@ void SubtargetEmitter::GenSchedClassTables(const CodeGenProcModel &ProcModel, } if (Writes.empty()) { // Check this processor's itinerary class resources. - for (RecIter II = ProcModel.ItinRWDefs.begin(), - IE = ProcModel.ItinRWDefs.end(); II != IE; ++II) { - RecVec Matched = (*II)->getValueAsListOfDefs("MatchedItinClasses"); - if (std::find(Matched.begin(), Matched.end(), SCI->ItinClassDef) + for (Record *I : ProcModel.ItinRWDefs) { + RecVec Matched = I->getValueAsListOfDefs("MatchedItinClasses"); + if (std::find(Matched.begin(), Matched.end(), SC.ItinClassDef) != Matched.end()) { - SchedModels.findRWs((*II)->getValueAsListOfDefs("OperandReadWrites"), + SchedModels.findRWs(I->getValueAsListOfDefs("OperandReadWrites"), Writes, Reads); break; } } if (Writes.empty()) { DEBUG(dbgs() << ProcModel.ModelName - << " does not have resources for class " << SCI->Name << '\n'); + << " does not have resources for class " << SC.Name << '\n'); } } // Sum resources across all operand writes. @@ -897,9 +890,9 @@ void SubtargetEmitter::GenSchedClassTables(const CodeGenProcModel &ProcModel, std::vector<MCWriteLatencyEntry> WriteLatencies; std::vector<std::string> WriterNames; std::vector<MCReadAdvanceEntry> ReadAdvanceEntries; - for (IdxIter WI = Writes.begin(), WE = Writes.end(); WI != WE; ++WI) { + for (unsigned W : Writes) { IdxVec WriteSeq; - SchedModels.expandRWSeqForProc(*WI, WriteSeq, /*IsRead=*/false, + SchedModels.expandRWSeqForProc(W, WriteSeq, /*IsRead=*/false, ProcModel); // For each operand, create a latency entry. @@ -915,11 +908,10 @@ void SubtargetEmitter::GenSchedClassTables(const CodeGenProcModel &ProcModel, } WLEntry.WriteResourceID = WriteID; - for (IdxIter WSI = WriteSeq.begin(), WSE = WriteSeq.end(); - WSI != WSE; ++WSI) { + for (unsigned WS : WriteSeq) { Record *WriteRes = - FindWriteResources(SchedModels.getSchedWrite(*WSI), ProcModel); + FindWriteResources(SchedModels.getSchedWrite(WS), ProcModel); // Mark the parent class as invalid for unsupported write types. if (WriteRes->getValueAsBit("Unsupported")) { @@ -981,16 +973,15 @@ void SubtargetEmitter::GenSchedClassTables(const CodeGenProcModel &ProcModel, if (ValidWrites.empty()) WriteIDs.push_back(0); else { - for (RecIter VWI = ValidWrites.begin(), VWE = ValidWrites.end(); - VWI != VWE; ++VWI) { - WriteIDs.push_back(SchedModels.getSchedRWIdx(*VWI, /*IsRead=*/false)); + for (Record *VW : ValidWrites) { + WriteIDs.push_back(SchedModels.getSchedRWIdx(VW, /*IsRead=*/false)); } } std::sort(WriteIDs.begin(), WriteIDs.end()); - for(IdxIter WI = WriteIDs.begin(), WE = WriteIDs.end(); WI != WE; ++WI) { + for(unsigned W : WriteIDs) { MCReadAdvanceEntry RAEntry; RAEntry.UseIdx = UseIdx; - RAEntry.WriteResourceID = *WI; + RAEntry.WriteResourceID = W; RAEntry.Cycles = ReadAdvance->getValueAsInt("Cycles"); ReadAdvanceEntries.push_back(RAEntry); } @@ -1130,7 +1121,7 @@ void SubtargetEmitter::EmitSchedClassTables(SchedClassTables &SchedTables, && "invalid class not first"); OS << " {DBGFIELD(\"InvalidSchedClass\") " << MCSchedClassDesc::InvalidNumMicroOps - << ", 0, 0, 0, 0, 0, 0, 0, 0},\n"; + << ", false, false, 0, 0, 0, 0, 0, 0},\n"; for (unsigned SCIdx = 1, SCEnd = SCTab.size(); SCIdx != SCEnd; ++SCIdx) { MCSchedClassDesc &MCDesc = SCTab[SCIdx]; @@ -1139,7 +1130,8 @@ void SubtargetEmitter::EmitSchedClassTables(SchedClassTables &SchedTables, if (SchedClass.Name.size() < 18) OS.indent(18 - SchedClass.Name.size()); OS << MCDesc.NumMicroOps - << ", " << MCDesc.BeginGroup << ", " << MCDesc.EndGroup + << ", " << ( MCDesc.BeginGroup ? "true" : "false" ) + << ", " << ( MCDesc.EndGroup ? "true" : "false" ) << ", " << format("%2d", MCDesc.WriteProcResIdx) << ", " << MCDesc.NumWriteProcResEntries << ", " << format("%2d", MCDesc.WriteLatencyIdx) @@ -1156,45 +1148,48 @@ void SubtargetEmitter::EmitSchedClassTables(SchedClassTables &SchedTables, void SubtargetEmitter::EmitProcessorModels(raw_ostream &OS) { // For each processor model. - for (CodeGenSchedModels::ProcIter PI = SchedModels.procModelBegin(), - PE = SchedModels.procModelEnd(); PI != PE; ++PI) { + for (const CodeGenProcModel &PM : SchedModels.procModels()) { // Emit processor resource table. - if (PI->hasInstrSchedModel()) - EmitProcessorResources(*PI, OS); - else if(!PI->ProcResourceDefs.empty()) - PrintFatalError(PI->ModelDef->getLoc(), "SchedMachineModel defines " + if (PM.hasInstrSchedModel()) + EmitProcessorResources(PM, OS); + else if(!PM.ProcResourceDefs.empty()) + PrintFatalError(PM.ModelDef->getLoc(), "SchedMachineModel defines " "ProcResources without defining WriteRes SchedWriteRes"); // Begin processor itinerary properties OS << "\n"; - OS << "static const llvm::MCSchedModel " << PI->ModelName << " = {\n"; - EmitProcessorProp(OS, PI->ModelDef, "IssueWidth", ','); - EmitProcessorProp(OS, PI->ModelDef, "MicroOpBufferSize", ','); - EmitProcessorProp(OS, PI->ModelDef, "LoopMicroOpBufferSize", ','); - EmitProcessorProp(OS, PI->ModelDef, "LoadLatency", ','); - EmitProcessorProp(OS, PI->ModelDef, "HighLatency", ','); - EmitProcessorProp(OS, PI->ModelDef, "MispredictPenalty", ','); - - OS << " " << (bool)(PI->ModelDef ? - PI->ModelDef->getValueAsBit("PostRAScheduler") : 0) - << ", // " << "PostRAScheduler\n"; - - OS << " " << (bool)(PI->ModelDef ? - PI->ModelDef->getValueAsBit("CompleteModel") : 0) - << ", // " << "CompleteModel\n"; - - OS << " " << PI->Index << ", // Processor ID\n"; - if (PI->hasInstrSchedModel()) - OS << " " << PI->ModelName << "ProcResources" << ",\n" - << " " << PI->ModelName << "SchedClasses" << ",\n" - << " " << PI->ProcResourceDefs.size()+1 << ",\n" + OS << "static const llvm::MCSchedModel " << PM.ModelName << " = {\n"; + EmitProcessorProp(OS, PM.ModelDef, "IssueWidth", ','); + EmitProcessorProp(OS, PM.ModelDef, "MicroOpBufferSize", ','); + EmitProcessorProp(OS, PM.ModelDef, "LoopMicroOpBufferSize", ','); + EmitProcessorProp(OS, PM.ModelDef, "LoadLatency", ','); + EmitProcessorProp(OS, PM.ModelDef, "HighLatency", ','); + EmitProcessorProp(OS, PM.ModelDef, "MispredictPenalty", ','); + + bool PostRAScheduler = + (PM.ModelDef ? PM.ModelDef->getValueAsBit("PostRAScheduler") : false); + + OS << " " << (PostRAScheduler ? "true" : "false") << ", // " + << "PostRAScheduler\n"; + + bool CompleteModel = + (PM.ModelDef ? PM.ModelDef->getValueAsBit("CompleteModel") : false); + + OS << " " << (CompleteModel ? "true" : "false") << ", // " + << "CompleteModel\n"; + + OS << " " << PM.Index << ", // Processor ID\n"; + if (PM.hasInstrSchedModel()) + OS << " " << PM.ModelName << "ProcResources" << ",\n" + << " " << PM.ModelName << "SchedClasses" << ",\n" + << " " << PM.ProcResourceDefs.size()+1 << ",\n" << " " << (SchedModels.schedClassEnd() - SchedModels.schedClassBegin()) << ",\n"; else OS << " nullptr, nullptr, 0, 0," << " // No instruction-level machine model.\n"; - if (PI->hasItineraries()) - OS << " " << PI->ItinsDef->getName() << "};\n"; + if (PM.hasItineraries()) + OS << " " << PM.ItinsDef->getName() << "};\n"; else OS << " nullptr}; // No Itinerary\n"; } @@ -1260,9 +1255,8 @@ void SubtargetEmitter::EmitSchedModel(raw_ostream &OS) { << "// Data tables for the new per-operand machine model.\n"; SchedClassTables SchedTables; - for (CodeGenSchedModels::ProcIter PI = SchedModels.procModelBegin(), - PE = SchedModels.procModelEnd(); PI != PE; ++PI) { - GenSchedClassTables(*PI, SchedTables); + for (const CodeGenProcModel &ProcModel : SchedModels.procModels()) { + GenSchedClassTables(ProcModel, SchedTables); } EmitSchedClassTables(SchedTables, OS); @@ -1274,7 +1268,7 @@ void SubtargetEmitter::EmitSchedModel(raw_ostream &OS) { OS << "#undef DBGFIELD"; } -void SubtargetEmitter::EmitSchedModelHelpers(std::string ClassName, +void SubtargetEmitter::EmitSchedModelHelpers(const std::string &ClassName, raw_ostream &OS) { OS << "unsigned " << ClassName << "\n::resolveSchedClass(unsigned SchedClass, const MachineInstr *MI," @@ -1282,60 +1276,52 @@ void SubtargetEmitter::EmitSchedModelHelpers(std::string ClassName, std::vector<Record*> Prologs = Records.getAllDerivedDefinitions("PredicateProlog"); std::sort(Prologs.begin(), Prologs.end(), LessRecord()); - for (std::vector<Record*>::const_iterator - PI = Prologs.begin(), PE = Prologs.end(); PI != PE; ++PI) { - OS << (*PI)->getValueAsString("Code") << '\n'; + for (Record *P : Prologs) { + OS << P->getValueAsString("Code") << '\n'; } IdxVec VariantClasses; - for (CodeGenSchedModels::SchedClassIter SCI = SchedModels.schedClassBegin(), - SCE = SchedModels.schedClassEnd(); SCI != SCE; ++SCI) { - if (SCI->Transitions.empty()) + for (const CodeGenSchedClass &SC : SchedModels.schedClasses()) { + if (SC.Transitions.empty()) continue; - VariantClasses.push_back(SCI->Index); + VariantClasses.push_back(SC.Index); } if (!VariantClasses.empty()) { OS << " switch (SchedClass) {\n"; - for (IdxIter VCI = VariantClasses.begin(), VCE = VariantClasses.end(); - VCI != VCE; ++VCI) { - const CodeGenSchedClass &SC = SchedModels.getSchedClass(*VCI); - OS << " case " << *VCI << ": // " << SC.Name << '\n'; + for (unsigned VC : VariantClasses) { + const CodeGenSchedClass &SC = SchedModels.getSchedClass(VC); + OS << " case " << VC << ": // " << SC.Name << '\n'; IdxVec ProcIndices; - for (std::vector<CodeGenSchedTransition>::const_iterator - TI = SC.Transitions.begin(), TE = SC.Transitions.end(); - TI != TE; ++TI) { + for (const CodeGenSchedTransition &T : SC.Transitions) { IdxVec PI; - std::set_union(TI->ProcIndices.begin(), TI->ProcIndices.end(), + std::set_union(T.ProcIndices.begin(), T.ProcIndices.end(), ProcIndices.begin(), ProcIndices.end(), std::back_inserter(PI)); ProcIndices.swap(PI); } - for (IdxIter PI = ProcIndices.begin(), PE = ProcIndices.end(); - PI != PE; ++PI) { + for (unsigned PI : ProcIndices) { OS << " "; - if (*PI != 0) - OS << "if (SchedModel->getProcessorID() == " << *PI << ") "; - OS << "{ // " << (SchedModels.procModelBegin() + *PI)->ModelName + if (PI != 0) + OS << "if (SchedModel->getProcessorID() == " << PI << ") "; + OS << "{ // " << (SchedModels.procModelBegin() + PI)->ModelName << '\n'; - for (std::vector<CodeGenSchedTransition>::const_iterator - TI = SC.Transitions.begin(), TE = SC.Transitions.end(); - TI != TE; ++TI) { - if (*PI != 0 && !std::count(TI->ProcIndices.begin(), - TI->ProcIndices.end(), *PI)) { + for (const CodeGenSchedTransition &T : SC.Transitions) { + if (PI != 0 && !std::count(T.ProcIndices.begin(), + T.ProcIndices.end(), PI)) { continue; } OS << " if ("; - for (RecIter RI = TI->PredTerm.begin(), RE = TI->PredTerm.end(); + for (RecIter RI = T.PredTerm.begin(), RE = T.PredTerm.end(); RI != RE; ++RI) { - if (RI != TI->PredTerm.begin()) + if (RI != T.PredTerm.begin()) OS << "\n && "; OS << "(" << (*RI)->getValueAsString("Predicate") << ")"; } OS << ")\n" - << " return " << TI->ToClassIdx << "; // " - << SchedModels.getSchedClass(TI->ToClassIdx).Name << '\n'; + << " return " << T.ToClassIdx << "; // " + << SchedModels.getSchedClass(T.ToClassIdx).Name << '\n'; } OS << " }\n"; - if (*PI == 0) + if (PI == 0) break; } if (SC.isInferred()) @@ -1375,9 +1361,8 @@ void SubtargetEmitter::ParseFeaturesFunction(raw_ostream &OS, OS << " InitMCProcessorInfo(CPU, FS);\n" << " const FeatureBitset& Bits = getFeatureBits();\n"; - for (unsigned i = 0; i < Features.size(); i++) { + for (Record *R : Features) { // Next record - Record *R = Features[i]; const std::string &Instance = R->getName(); const std::string &Value = R->getValueAsString("Value"); const std::string &Attribute = R->getValueAsString("Attribute"); @@ -1403,15 +1388,15 @@ void SubtargetEmitter::run(raw_ostream &OS) { emitSourceFileHeader("Subtarget Enumeration Source Fragment", OS); OS << "\n#ifdef GET_SUBTARGETINFO_ENUM\n"; - OS << "#undef GET_SUBTARGETINFO_ENUM\n"; + OS << "#undef GET_SUBTARGETINFO_ENUM\n\n"; OS << "namespace llvm {\n"; - Enumeration(OS, "SubtargetFeature"); - OS << "} // end llvm namespace\n"; + Enumeration(OS); + OS << "} // end namespace llvm\n\n"; OS << "#endif // GET_SUBTARGETINFO_ENUM\n\n"; OS << "\n#ifdef GET_SUBTARGETINFO_MC_DESC\n"; - OS << "#undef GET_SUBTARGETINFO_MC_DESC\n"; + OS << "#undef GET_SUBTARGETINFO_MC_DESC\n\n"; OS << "namespace llvm {\n"; #if 0 @@ -1424,7 +1409,7 @@ void SubtargetEmitter::run(raw_ostream &OS) { EmitSchedModel(OS); OS << "\n"; #if 0 - OS << "}\n"; + OS << "} // end anonymous namespace\n\n"; #endif // MCInstrInfo initialization routine. @@ -1454,22 +1439,22 @@ void SubtargetEmitter::run(raw_ostream &OS) { OS << "0, 0, 0"; OS << ");\n}\n\n"; - OS << "} // end llvm namespace\n"; + OS << "} // end namespace llvm\n\n"; OS << "#endif // GET_SUBTARGETINFO_MC_DESC\n\n"; OS << "\n#ifdef GET_SUBTARGETINFO_TARGET_DESC\n"; - OS << "#undef GET_SUBTARGETINFO_TARGET_DESC\n"; + OS << "#undef GET_SUBTARGETINFO_TARGET_DESC\n\n"; OS << "#include \"llvm/Support/Debug.h\"\n"; - OS << "#include \"llvm/Support/raw_ostream.h\"\n"; + OS << "#include \"llvm/Support/raw_ostream.h\"\n\n"; ParseFeaturesFunction(OS, NumFeatures, NumProcs); OS << "#endif // GET_SUBTARGETINFO_TARGET_DESC\n\n"; // Create a TargetSubtargetInfo subclass to hide the MC layer initialization. OS << "\n#ifdef GET_SUBTARGETINFO_HEADER\n"; - OS << "#undef GET_SUBTARGETINFO_HEADER\n"; + OS << "#undef GET_SUBTARGETINFO_HEADER\n\n"; std::string ClassName = Target + "GenSubtargetInfo"; OS << "namespace llvm {\n"; @@ -1484,14 +1469,14 @@ void SubtargetEmitter::run(raw_ostream &OS) { << " DFAPacketizer *createDFAPacketizer(const InstrItineraryData *IID)" << " const;\n" << "};\n"; - OS << "} // end llvm namespace\n"; + OS << "} // end namespace llvm\n\n"; OS << "#endif // GET_SUBTARGETINFO_HEADER\n\n"; OS << "\n#ifdef GET_SUBTARGETINFO_CTOR\n"; - OS << "#undef GET_SUBTARGETINFO_CTOR\n"; + OS << "#undef GET_SUBTARGETINFO_CTOR\n\n"; - OS << "#include \"llvm/CodeGen/TargetSchedule.h\"\n"; + OS << "#include \"llvm/CodeGen/TargetSchedule.h\"\n\n"; OS << "namespace llvm {\n"; OS << "extern const llvm::SubtargetFeatureKV " << Target << "FeatureKV[];\n"; OS << "extern const llvm::SubtargetFeatureKV " << Target << "SubTypeKV[];\n"; @@ -1536,7 +1521,7 @@ void SubtargetEmitter::run(raw_ostream &OS) { EmitSchedModelHelpers(ClassName, OS); - OS << "} // end llvm namespace\n"; + OS << "} // end namespace llvm\n\n"; OS << "#endif // GET_SUBTARGETINFO_CTOR\n\n"; } @@ -1548,4 +1533,4 @@ void EmitSubtarget(RecordKeeper &RK, raw_ostream &OS) { SubtargetEmitter(RK, CGTarget).run(OS); } -} // end llvm namespace +} // end namespace llvm diff --git a/utils/TableGen/TableGen.cpp b/utils/TableGen/TableGen.cpp index bcc594d69a1d9..24dbe5d3a2801 100644 --- a/utils/TableGen/TableGen.cpp +++ b/utils/TableGen/TableGen.cpp @@ -43,7 +43,8 @@ enum ActionType { PrintSets, GenOptParserDefs, GenCTags, - GenAttributes + GenAttributes, + GenSearchableTables, }; namespace { @@ -89,6 +90,8 @@ namespace { "Generate ctags-compatible index"), clEnumValN(GenAttributes, "gen-attrs", "Generate attributes"), + clEnumValN(GenSearchableTables, "gen-searchable-tables", + "Generate generic binary-searchable table"), clEnumValEnd)); cl::opt<std::string> @@ -172,6 +175,9 @@ bool LLVMTableGenMain(raw_ostream &OS, RecordKeeper &Records) { case GenAttributes: EmitAttributes(Records, OS); break; + case GenSearchableTables: + EmitSearchableTables(Records, OS); + break; } return false; @@ -179,7 +185,7 @@ bool LLVMTableGenMain(raw_ostream &OS, RecordKeeper &Records) { } int main(int argc, char **argv) { - sys::PrintStackTraceOnErrorSignal(); + sys::PrintStackTraceOnErrorSignal(argv[0]); PrettyStackTraceProgram X(argc, argv); cl::ParseCommandLineOptions(argc, argv); diff --git a/utils/TableGen/TableGenBackends.h b/utils/TableGen/TableGenBackends.h index d9dd3d157697b..51e017fe65942 100644 --- a/utils/TableGen/TableGenBackends.h +++ b/utils/TableGen/TableGenBackends.h @@ -79,6 +79,7 @@ void EmitMapTable(RecordKeeper &RK, raw_ostream &OS); void EmitOptParser(RecordKeeper &RK, raw_ostream &OS); void EmitCTags(RecordKeeper &RK, raw_ostream &OS); void EmitAttributes(RecordKeeper &RK, raw_ostream &OS); +void EmitSearchableTables(RecordKeeper &RK, raw_ostream &OS); } // End llvm namespace diff --git a/utils/TableGen/X86DisassemblerTables.cpp b/utils/TableGen/X86DisassemblerTables.cpp index ad36dc427a562..5b710e4461507 100644 --- a/utils/TableGen/X86DisassemblerTables.cpp +++ b/utils/TableGen/X86DisassemblerTables.cpp @@ -285,7 +285,7 @@ static inline bool inheritsFrom(InstructionContext child, return false; case IC_EVEX_L_W_K: case IC_EVEX_L_W_B: - case IC_EVEX_L_W_K_B: + case IC_EVEX_L_W_K_B: case IC_EVEX_L_W_XS_K: case IC_EVEX_L_W_XS_B: case IC_EVEX_L_W_XS_K_B: diff --git a/utils/TableGen/X86RecognizableInstr.cpp b/utils/TableGen/X86RecognizableInstr.cpp index 8a5ae12f67fb2..ca937d09726d5 100644 --- a/utils/TableGen/X86RecognizableInstr.cpp +++ b/utils/TableGen/X86RecognizableInstr.cpp @@ -225,7 +225,6 @@ RecognizableInstr::RecognizableInstr(DisassemblerTables &tables, CD8_Scale = byteFromRec(Rec, "CD8_Scale"); Name = Rec->getName(); - AsmString = Rec->getValueAsString("AsmString"); Operands = &insn.Operands.OperandList; @@ -477,7 +476,7 @@ void RecognizableInstr::adjustOperandEncoding(OperandEncoding &encoding) { void RecognizableInstr::handleOperand(bool optional, unsigned &operandIndex, unsigned &physicalOperandIndex, - unsigned &numPhysicalOperands, + unsigned numPhysicalOperands, const unsigned *operandMapping, OperandEncoding (*encodingFromString) (const std::string&, @@ -562,6 +561,7 @@ void RecognizableInstr::emitInstructionSpecifier() { // physicalOperandIndex should always be < numPhysicalOperands unsigned physicalOperandIndex = 0; +#ifndef NDEBUG // Given the set of prefix bits, how many additional operands does the // instruction have? unsigned additionalOperands = 0; @@ -569,6 +569,7 @@ void RecognizableInstr::emitInstructionSpecifier() { ++additionalOperands; if (HasEVEX_K) ++additionalOperands; +#endif switch (Form) { default: llvm_unreachable("Unhandled form"); @@ -584,11 +585,9 @@ void RecognizableInstr::emitInstructionSpecifier() { return; case X86Local::RawFrm: // Operand 1 (optional) is an address or immediate. - // Operand 2 (optional) is an immediate. - assert(numPhysicalOperands <= 2 && + assert(numPhysicalOperands <= 1 && "Unexpected number of operands for RawFrm"); HANDLE_OPTIONAL(relocation) - HANDLE_OPTIONAL(immediate) break; case X86Local::RawFrmMemOffs: // Operand 1 is an address. @@ -800,8 +799,8 @@ void RecognizableInstr::emitInstructionSpecifier() { case X86Local::MRM_F1: case X86Local::MRM_F2: case X86Local::MRM_F3: case X86Local::MRM_F4: case X86Local::MRM_F5: case X86Local::MRM_F6: case X86Local::MRM_F7: case X86Local::MRM_F9: case X86Local::MRM_FA: - case X86Local::MRM_FB: case X86Local::MRM_FC: case X86Local::MRM_FD: - case X86Local::MRM_FE: case X86Local::MRM_FF: + case X86Local::MRM_FB: case X86Local::MRM_FC: case X86Local::MRM_FD: + case X86Local::MRM_FE: case X86Local::MRM_FF: // Ignored. break; } @@ -1024,19 +1023,19 @@ OperandType RecognizableInstr::typeFromString(const std::string &s, TYPE("VK32WM", TYPE_VK32) TYPE("VK64", TYPE_VK64) TYPE("VK64WM", TYPE_VK64) - TYPE("GR16_NOAX", TYPE_Rv) TYPE("GR32_NOAX", TYPE_Rv) - TYPE("GR64_NOAX", TYPE_R64) - TYPE("vx32mem", TYPE_M32) - TYPE("vx32xmem", TYPE_M32) - TYPE("vy32mem", TYPE_M32) - TYPE("vy32xmem", TYPE_M32) - TYPE("vz32mem", TYPE_M32) TYPE("vx64mem", TYPE_M64) + TYPE("vx128mem", TYPE_M128) + TYPE("vx256mem", TYPE_M256) + TYPE("vy128mem", TYPE_M128) + TYPE("vy256mem", TYPE_M256) TYPE("vx64xmem", TYPE_M64) - TYPE("vy64mem", TYPE_M64) - TYPE("vy64xmem", TYPE_M64) - TYPE("vz64mem", TYPE_M64) + TYPE("vx128xmem", TYPE_M128) + TYPE("vx256xmem", TYPE_M256) + TYPE("vy128xmem", TYPE_M128) + TYPE("vy256xmem", TYPE_M256) + TYPE("vy512mem", TYPE_M512) + TYPE("vz512mem", TYPE_M512) TYPE("BNDR", TYPE_BNDR) errs() << "Unhandled type string " << s << "\n"; llvm_unreachable("Unhandled type string"); @@ -1220,16 +1219,18 @@ RecognizableInstr::memoryEncodingFromString(const std::string &s, ENCODING("opaque48mem", ENCODING_RM) ENCODING("opaque80mem", ENCODING_RM) ENCODING("opaque512mem", ENCODING_RM) - ENCODING("vx32mem", ENCODING_RM) - ENCODING("vx32xmem", ENCODING_RM) - ENCODING("vy32mem", ENCODING_RM) - ENCODING("vy32xmem", ENCODING_RM) - ENCODING("vz32mem", ENCODING_RM) ENCODING("vx64mem", ENCODING_RM) + ENCODING("vx128mem", ENCODING_RM) + ENCODING("vx256mem", ENCODING_RM) + ENCODING("vy128mem", ENCODING_RM) + ENCODING("vy256mem", ENCODING_RM) ENCODING("vx64xmem", ENCODING_RM) - ENCODING("vy64mem", ENCODING_RM) - ENCODING("vy64xmem", ENCODING_RM) - ENCODING("vz64mem", ENCODING_RM) + ENCODING("vx128xmem", ENCODING_RM) + ENCODING("vx256xmem", ENCODING_RM) + ENCODING("vy128xmem", ENCODING_RM) + ENCODING("vy256xmem", ENCODING_RM) + ENCODING("vy512mem", ENCODING_RM) + ENCODING("vz512mem", ENCODING_RM) errs() << "Unhandled memory encoding " << s << "\n"; llvm_unreachable("Unhandled memory encoding"); } @@ -1288,9 +1289,7 @@ RecognizableInstr::opcodeModifierEncodingFromString(const std::string &s, ENCODING("GR64", ENCODING_RO) ENCODING("GR16", ENCODING_Rv) ENCODING("GR8", ENCODING_RB) - ENCODING("GR16_NOAX", ENCODING_Rv) ENCODING("GR32_NOAX", ENCODING_Rv) - ENCODING("GR64_NOAX", ENCODING_RO) errs() << "Unhandled opcode modifier encoding " << s << "\n"; llvm_unreachable("Unhandled opcode modifier encoding"); } diff --git a/utils/TableGen/X86RecognizableInstr.h b/utils/TableGen/X86RecognizableInstr.h index 28e10535508d7..f6f50065f7049 100644 --- a/utils/TableGen/X86RecognizableInstr.h +++ b/utils/TableGen/X86RecognizableInstr.h @@ -19,7 +19,6 @@ #include "CodeGenTarget.h" #include "X86DisassemblerTables.h" -#include "llvm/ADT/SmallVector.h" #include "llvm/Support/DataTypes.h" #include "llvm/TableGen/Record.h" @@ -87,8 +86,6 @@ private: /// The instruction name as listed in the tables std::string Name; - /// The AT&T AsmString for the instruction - std::string AsmString; /// Indicates whether the instruction should be emitted into the decode /// tables; regardless, it will be emitted into the instruction info table @@ -179,7 +176,7 @@ private: void handleOperand(bool optional, unsigned &operandIndex, unsigned &physicalOperandIndex, - unsigned &numPhysicalOperands, + unsigned numPhysicalOperands, const unsigned *operandMapping, OperandEncoding (*encodingFromString) (const std::string&, diff --git a/utils/TableGen/module.modulemap b/utils/TableGen/module.modulemap deleted file mode 100644 index 8871bbfd4a2f7..0000000000000 --- a/utils/TableGen/module.modulemap +++ /dev/null @@ -1,4 +0,0 @@ -module TableGen { - umbrella "." - module * { export * } -} diff --git a/utils/abtest/abtest.py b/utils/abtest/abtest.py new file mode 100755 index 0000000000000..ad6a3e0ea8d22 --- /dev/null +++ b/utils/abtest/abtest.py @@ -0,0 +1,234 @@ +#!/usr/bin/env python +# +# Given a previous good compile narrow down miscompiles. +# Expects two directories named "before" and "after" each containing a set of +# assembly or object files where the "after" version is assumed to be broken. +# You also have to provide a script called "link_test". It is called with a list +# of files which should be linked together and result tested. "link_test" should +# returns with exitcode 0 if the linking and testing succeeded. +# +# abtest.py operates by taking all files from the "before" directory and +# in each step replacing one of them with a file from the "bad" directory. +# +# Additionally you can perform the same steps with a single .s file. In this +# mode functions are identified by "# -- Begin FunctionName" and +# "# -- End FunctionName" markers. The abtest.py then takes all functions from +# the file in the "before" directory and replaces one function with the +# corresponding function from the "bad" file in each step. +# +# Example usage to identify miscompiled files: +# 1. Create a link_test script, make it executable. Simple Example: +# clang "$@" -o /tmp/test && /tmp/test || echo "PROBLEM" +# 2. Run the script to figure out which files are miscompiled: +# > ./abtest.py +# somefile.s: ok +# someotherfile.s: skipped: same content +# anotherfile.s: failed: './link_test' exitcode != 0 +# ... +# Example usage to identify miscompiled functions inside a file: +# 3. First you have to mark begin and end of the functions. +# The script comes with some examples called mark_xxx.py. +# Unfortunately this is very specific to your environment and it is likely +# that you have to write a custom version for your environment. +# > for i in before/*.s after/*.s; do mark_xxx.py $i; done +# 4. Run the tests on a single file (assuming before/file.s and +# after/file.s exist) +# > ./abtest.py file.s +# funcname1 [0/XX]: ok +# funcname2 [1/XX]: ok +# funcname3 [2/XX]: skipped: same content +# funcname4 [3/XX]: failed: './link_test' exitcode != 0 +# ... +from fnmatch import filter +from sys import stderr +import argparse +import filecmp +import os +import subprocess +import sys + +LINKTEST="./link_test" +ESCAPE="\033[%sm" +BOLD=ESCAPE % "1" +RED=ESCAPE % "31" +NORMAL=ESCAPE % "0" +FAILED=RED+"failed"+NORMAL + +def find(dir, file_filter=None): + files = [walkdir[0]+"/"+file for walkdir in os.walk(dir) for file in walkdir[2]] + if file_filter != None: + files = filter(files, file_filter) + return files + +def error(message): + stderr.write("Error: %s\n" % (message,)) + +def warn(message): + stderr.write("Warning: %s\n" % (message,)) + +def extract_functions(file): + functions = [] + in_function = None + for line in open(file): + if line.startswith("# -- Begin "): + if in_function != None: + warn("Missing end of function %s" % (in_function,)) + funcname = line[12:-1] + in_function = funcname + text = line + elif line.startswith("# -- End "): + function_name = line[10:-1] + if in_function != function_name: + warn("End %s does not match begin %s" % (function_name, in_function)) + else: + text += line + functions.append( (in_function, text) ) + in_function = None + elif in_function != None: + text += line + return functions + +def replace_function(file, function, replacement, dest): + out = open(dest, "w") + skip = False + found = False + in_function = None + for line in open(file): + if line.startswith("# -- Begin "): + if in_function != None: + warn("Missing end of function %s" % (in_function,)) + funcname = line[12:-1] + in_function = funcname + if in_function == function: + out.write(replacement) + skip = True + elif line.startswith("# -- End "): + function_name = line[10:-1] + if in_function != function_name: + warn("End %s does not match begin %s" % (function_name, in_function)) + in_function = None + if skip: + skip = False + continue + if not skip: + out.write(line) + +def announce_test(name): + stderr.write("%s%s%s: " % (BOLD, name, NORMAL)) + stderr.flush() + +def announce_result(result, info): + stderr.write(result) + if info != "": + stderr.write(": %s" % info) + stderr.write("\n") + stderr.flush() + +def testrun(files): + linkline="%s %s" % (LINKTEST, " ".join(files),) + res = subprocess.call(linkline, shell=True) + if res != 0: + announce_result(FAILED, "'%s' exitcode != 0" % LINKTEST) + return False + else: + announce_result("ok", "") + return True + +def check_files(): + """Check files mode""" + for i in range(0, len(NO_PREFIX)): + f = NO_PREFIX[i] + b=baddir+"/"+f + if b not in BAD_FILES: + warn("There is no corresponding file to '%s' in %s" \ + % (gooddir+"/"+f, baddir)) + continue + + announce_test(f + " [%s/%s]" % (i+1, len(NO_PREFIX))) + + # combine files (everything from good except f) + testfiles=[] + skip=False + for c in NO_PREFIX: + badfile = baddir+"/"+c + goodfile = gooddir+"/"+c + if c == f: + testfiles.append(badfile) + if filecmp.cmp(goodfile, badfile): + announce_result("skipped", "same content") + skip = True + break + else: + testfiles.append(goodfile) + if skip: + continue + testrun(testfiles) + +def check_functions_in_file(base, goodfile, badfile): + functions = extract_functions(goodfile) + if len(functions) == 0: + warn("Couldn't find any function in %s, missing annotations?" % (goodfile,)) + return + badfunctions = dict(extract_functions(badfile)) + if len(functions) == 0: + warn("Couldn't find any function in %s, missing annotations?" % (badfile,)) + return + + COMBINED="/tmp/combined.s" + i = 0 + for (func,func_text) in functions: + announce_test(func + " [%s/%s]" % (i+1, len(functions))) + i+=1 + if func not in badfunctions: + warn("Function '%s' missing from bad file" % func) + continue + if badfunctions[func] == func_text: + announce_result("skipped", "same content") + continue + replace_function(goodfile, func, badfunctions[func], COMBINED) + testfiles=[] + for c in NO_PREFIX: + if c == base: + testfiles.append(COMBINED) + continue + testfiles.append(gooddir + "/" + c) + + testrun(testfiles) + +parser = argparse.ArgumentParser() +parser.add_argument('--a', dest='dir_a', default='before') +parser.add_argument('--b', dest='dir_b', default='after') +parser.add_argument('--insane', help='Skip sanity check', action='store_true') +parser.add_argument('file', metavar='file', nargs='?') +config = parser.parse_args() + +gooddir=config.dir_a +baddir=config.dir_b + +BAD_FILES=find(baddir, "*") +GOOD_FILES=find(gooddir, "*") +NO_PREFIX=sorted([x[len(gooddir)+1:] for x in GOOD_FILES]) + +# "Checking whether build environment is sane ..." +if not config.insane: + announce_test("sanity check") + if not os.access(LINKTEST, os.X_OK): + error("Expect '%s' to be present and executable" % (LINKTEST,)) + exit(1) + + res = testrun(GOOD_FILES) + if not res: + # "build environment is grinning and holding a spatula. Guess not." + linkline="%s %s" % (LINKTEST, " ".join(GOOD_FILES),) + stderr.write("\n%s\n\n" % linkline) + stderr.write("Returned with exitcode != 0\n") + sys.exit(1) + +if config.file is not None: + # File exchange mode + goodfile = gooddir+"/"+config.file + badfile = baddir+"/"+config.file + check_functions_in_file(config.file, goodfile, badfile) +else: + # Function exchange mode + check_files() diff --git a/utils/abtest/mark_aarch64fns.py b/utils/abtest/mark_aarch64fns.py new file mode 100755 index 0000000000000..652014792849b --- /dev/null +++ b/utils/abtest/mark_aarch64fns.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# +# Mark functions in an arm assembly file. This is done by surrounding the +# function with "# -- Begin Name" and "# -- End Name" +# (This script is designed for aarch64 ios assembly syntax) +import sys +import re + +inp = open(sys.argv[1], "r").readlines() + +# First pass +linenum = 0 +INVALID=-100 +last_align = INVALID +last_code = INVALID +last_globl = INVALID +last_globl_name = None +begin = INVALID +in_text_section = False +begins = dict() +for line in inp: + linenum += 1 + if re.search(r'.section\s+__TEXT,__text,regular,pure_instructions', line): + in_text_section = True + continue + elif ".section" in line: + in_text_section = False + continue + + if not in_text_section: + continue + + if ".align" in line: + last_align = linenum + gl = re.search(r'.globl\s+(\w+)', line) + if gl: + last_globl_name = gl.group(1) + last_globl = linenum + m = re.search(r'^(\w+):', line) + if m and begin == INVALID: + labelname = m.group(1) + if last_globl+2 == linenum and last_globl_name == labelname: + begin = last_globl + funcname = labelname + if line == "\n" and begin != INVALID: + end = linenum + triple = (funcname, begin, end) + begins[begin] = triple + begin = INVALID + +# Second pass: Mark +out = open(sys.argv[1], "w") +in_func = None +linenum = 0 +for line in inp: + linenum += 1 + if in_func is not None and linenum == end: + out.write("# -- End %s\n" % in_func) + in_func = None + + triple = begins.get(linenum) + if triple is not None: + in_func, begin, end = triple + out.write("# -- Begin %s\n" % in_func) + out.write(line) diff --git a/utils/abtest/mark_armfns.py b/utils/abtest/mark_armfns.py new file mode 100755 index 0000000000000..0edf42e8a83c4 --- /dev/null +++ b/utils/abtest/mark_armfns.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +# +# Mark functions in an arm assembly file. This is done by surrounding the +# function with "# -- Begin Name" and "# -- End Name" +# (This script is designed for arm ios assembly syntax) +import sys +import re + +inp = open(sys.argv[1], "r").readlines() + +# First pass +linenum = 0 +INVALID=-100 +last_align = INVALID +last_code = INVALID +last_globl = INVALID +begin = INVALID +begins = dict() +for line in inp: + linenum += 1 + if ".align" in line: + last_align = linenum + if ".code" in line: + last_code = linenum + if ".globl" in line: + last_globl = linenum + m = re.search(r'.thumb_func\s+(\w+)', line) + if m: + funcname = m.group(1) + if last_code == last_align+1 and (linenum - last_code) < 4: + begin = last_align + if last_globl+1 == last_align: + begin = last_globl + if line == "\n" and begin != INVALID: + end = linenum + triple = (funcname, begin, end) + begins[begin] = triple + begin = INVALID + +# Second pass: Mark +out = open(sys.argv[1], "w") +in_func = None +linenum = 0 +for line in inp: + linenum += 1 + if in_func is not None and linenum == end: + out.write("# -- End %s\n" % in_func) + in_func = None + + triple = begins.get(linenum) + if triple is not None: + in_func, begin, end = triple + out.write("# -- Begin %s\n" % in_func) + out.write(line) diff --git a/utils/buildit/GNUmakefile b/utils/buildit/GNUmakefile deleted file mode 100644 index fc5578a68464e..0000000000000 --- a/utils/buildit/GNUmakefile +++ /dev/null @@ -1,132 +0,0 @@ -# LLVM LOCAL file build machinery -# LLVM Compiler Makefile for use by buildit. -# -# This makefile is intended only for use with B&I buildit. For "normal" builds -# use the conventional top-level makefile. -# -# You can specify TARGETS=ppc (or i386) on the buildit command line to limit the -# build to just one target. The default is for ppc and i386. The compiler -# targeted at this host gets built anyway, but not installed unless it's listed -# in TARGETS. - -# Include the set of standard Apple makefile definitions. -ifndef CoreOSMakefiles -CoreOSMakefiles = $(MAKEFILEPATH)/CoreOS -endif -include $(CoreOSMakefiles)/Standard/Standard.make - -# Enable Apple extensions to (gnu)make. -USE_APPLE_PB_SUPPORT = all - -RC_ARCHS := ppc i386 -HOSTS = $(RC_ARCHS) -targets = echo $(RC_ARCHS) -TARGETS := $(shell $(targets)) - -SRCROOT = . - -SRC = $(shell cd $(SRCROOT) && pwd | sed s,/private,,) -OBJROOT = $(SRC)/obj -SYMROOT = $(OBJROOT)/../sym -DSTROOT = $(OBJROOT)/../dst - -####################################################################### - -PREFIX = /usr/local - -# Unless assertions are forced on in the GMAKE command line, disable them. -ifndef ENABLE_ASSERTIONS -ENABLE_ASSERTIONS := no -endif - -# Default is optimized build. -ifeq ($(LLVM_DEBUG),1) -LLVM_OPTIMIZED := no -else -LLVM_OPTIMIZED := yes -endif - -# Default to do a native build, not a cross-build for an ARM host or simulator. -ARM_HOSTED_BUILD := no -IOS_SIM_BUILD := no - -ifndef RC_ProjectSourceVersion -RC_ProjectSourceVersion = 9999 -endif - -ifndef RC_ProjectSourceSubversion -RC_ProjectSourceSubversion = 0 -endif - -# NOTE : Always put version numbers at the end because they are optional. -install: $(OBJROOT) $(SYMROOT) $(DSTROOT) - cd $(OBJROOT) && \ - $(SRC)/utils/buildit/build_llvm "$(RC_ARCHS)" "$(TARGETS)" \ - $(SRC) $(PREFIX) $(DSTROOT) $(SYMROOT) \ - $(ENABLE_ASSERTIONS) $(LLVM_OPTIMIZED) \ - $(ARM_HOSTED_BUILD) $(IOS_SIM_BUILD) \ - $(RC_ProjectSourceVersion) $(RC_ProjectSourceSubversion) - -EmbeddedHosted: - $(MAKE) ARM_HOSTED_BUILD=yes PREFIX=/usr/local install - -# When building for the iOS simulator, MACOSX_DEPLOYMENT_TARGET is not set -# by default, but it needs to be set when building tools that run on the host -# (e.g., tblgen), so set it here. -EmbeddedSim: - export MACOSX_DEPLOYMENT_TARGET=`sw_vers -productVersion`; \ - $(MAKE) IOS_SIM_BUILD=yes PREFIX=$(SDKROOT)/usr/local install - -Embedded: - ARM_PLATFORM=`xcodebuild -version -sdk iphoneos PlatformPath` && \ - $(MAKE) DSTROOT=$(DSTROOT)$$ARM_PLATFORM/Developer install - -# installhdrs does nothing, because the headers aren't useful until -# the compiler is installed. -installhdrs: - -# We build and install in one shell script. -build: - -installsrc: - @echo - @echo ++++++++++++++++++++++ - @echo + Installing sources + - @echo ++++++++++++++++++++++ - @echo - if [ $(SRCROOT) != . ]; then \ - $(PAX) -rw . $(SRCROOT); \ - fi - find -d "$(SRCROOT)" \( -type d -a -name .svn -o \ - -type f -a -name .DS_Store -o \ - -name \*~ -o -name .\#\* \) \ - -exec rm -rf {} \; - rm -rf "$(SRCROOT)/test" - -####################################################################### - -clean: - @echo - @echo ++++++++++++ - @echo + Cleaning + - @echo ++++++++++++ - @echo - @if [ -d $(OBJROOT) -a "$(OBJROOT)" != / ]; then \ - echo '*** DELETING ' $(OBJROOT); \ - rm -rf $(OBJROOT); \ - fi - @if [ -d $(SYMROOT) -a "$(SYMROOT)" != / ]; then \ - echo '*** DELETING ' $(SYMROOT); \ - rm -rf $(SYMROOT); \ - fi - @if [ -d $(DSTROOT) -a "$(DSTROOT)" != / ]; then \ - echo '*** DELETING ' $(DSTROOT); \ - rm -rf $(DSTROOT); \ - fi - -####################################################################### - -$(OBJROOT) $(SYMROOT) $(DSTROOT): - mkdir -p $@ - -.PHONY: install installsrc clean EmbeddedHosted EmbeddedSim Embedded diff --git a/utils/buildit/build_llvm b/utils/buildit/build_llvm deleted file mode 100755 index bc609e909a92b..0000000000000 --- a/utils/buildit/build_llvm +++ /dev/null @@ -1,361 +0,0 @@ -#!/bin/sh -# LLVM LOCAL file B&I - -set -x - -# Build LLVM the "Apple way". -# Parameters: - -# The first parameter is a space-separated list of the architectures the -# compilers will run on. For instance, "ppc i386". If the current machine -# isn't in the list, it will (effectively) be added. -HOSTS="$1" - -# The second parameter is a space-separated list of the architectures the -# compilers will generate code for. If the current machine isn't in the list, a -# compiler for it will get built anyway, but won't be installed. -# FIXME: The list of targets is currently hard-coded and TARGETS is not used. -TARGETS="$2" - -# The third parameter is the path to the compiler sources. There should be a -# shell script named 'configure' in this directory. This script makes a copy... -ORIG_SRC_DIR="$3" - -# The fourth parameter is the location where the LLVM will be installed. You can -# move it once it's built, so this mostly controls the layout of $DEST_DIR. -DEST_ROOT="$4" - -# The fifth parameter is the place where the compiler will be copied once it's -# built. -DEST_DIR="$5" - -# The sixth parameter is a directory in which to place information (like -# unstripped executables and generated source files) helpful in debugging the -# resulting compiler. -SYM_DIR="$6" - -# The seventh parameter is a yes/no that indicates whether assertions should be -# enabled in the LLVM libs/tools. -LLVM_ASSERTIONS="$7" - -# The eighth parameter is a yes/no that indicates whether this is an optimized -# build. -LLVM_OPTIMIZED="$8" - -# A yes/no parameter that controls whether to cross-build for an ARM host. -ARM_HOSTED_BUILD="$9" - -# A yes/no parameter that controls whether to cross-build for the iOS simulator -IOS_SIM_BUILD="${10}" - -# The version number of the submission, e.g. 1007. -LLVM_SUBMIT_VERSION="${11}" - -# The subversion number of the submission, e.g. 03. -LLVM_SUBMIT_SUBVERSION="${12}" - -# The current working directory is where the build will happen. It may already -# contain a partial result of an interrupted build, in which case this script -# will continue where it left off. -DIR=`pwd` - -DARWIN_VERS=`uname -r | sed 's/\..*//'` -echo DARWIN_VERS = $DARWIN_VERS - -################################################################################ -# Run the build. - -# Create the source tree we'll actually use to build, deleting -# tcl since it doesn't actually build properly in a cross environment -# and we don't really need it. -SRC_DIR=$DIR/src -rm -rf $SRC_DIR || exit 1 -mkdir $SRC_DIR || exit 1 -ln -s $ORIG_SRC_DIR/* $SRC_DIR/ || exit 1 -# We can't use the top-level Makefile as-is. Remove the soft link: -rm $SRC_DIR/Makefile || exit 1 -# Now create our own by editing the top-level Makefile, deleting every line marked "Apple-style": -sed -e '/[Aa]pple-style/d' -e '/include.*GNUmakefile/d' $ORIG_SRC_DIR/Makefile > $SRC_DIR/Makefile || exit 1 - -SUBVERSION=`echo $RC_ProjectSourceVersion | sed -e 's/.*\.\([0-9]*\).*/\1/'` -if [ "x$SUBVERSION" != "x$RC_ProjectSourceVersion" ]; then - LLVM_SUBMIT_SUBVERSION=`printf "%02d" $SUBVERSION` - RC_ProjectSourceVersion=`echo $RC_ProjectSourceVersion | sed -e 's/\..*//'` - LLVM_SUBMIT_VERSION=$RC_ProjectSourceVersion -fi -if [ "x$LLVM_SUBMIT_SUBVERSION" = "x00" -o "x$LLVM_SUBMIT_SUBVERSION" = "x0" ]; then - LLVM_VERSION="$LLVM_SUBMIT_VERSION" -else - LLVM_VERSION="$LLVM_SUBMIT_VERSION-$LLVM_SUBMIT_SUBVERSION" -fi - -SDKROOT_PATH=`xcodebuild -version -sdk $SDKROOT Path` - -# Figure out how many make processes to run. -SYSCTL=`sysctl -n hw.activecpu` -# sysctl -n hw.* does not work when invoked via B&I chroot /BuildRoot. -# Builders can default to 2, since even if they are single processor, -# nothing else is running on the machine. -if [ -z "$SYSCTL" ]; then - SYSCTL=2 -fi -JOBS_FLAG="-j $SYSCTL" - -COMMON_CONFIGURE_OPTS="\ - --prefix=$DEST_DIR$DEST_ROOT \ - --enable-assertions=$LLVM_ASSERTIONS \ - --enable-optimized=$LLVM_OPTIMIZED \ - --disable-bindings \ - --disable-zlib \ - --enable-terminfo=no" - -COMMON_MAKEFLAGS="\ - UNIVERSAL=1 \ - UNIVERSAL_SDK_PATH=$SDKROOT_PATH \ - NO_RUNTIME_LIBS=1 \ - DISABLE_EDIS=1 \ - REQUIRES_RTTI=1 \ - DEBUG_SYMBOLS=1 \ - LLVM_SUBMIT_VERSION=$LLVM_SUBMIT_VERSION \ - LLVM_SUBMIT_SUBVERSION=$LLVM_SUBMIT_SUBVERSION \ - VERBOSE=1" - -# Build the LLVM tree universal. -mkdir -p $DIR/obj-llvm || exit 1 -cd $DIR/obj-llvm || exit 1 - -if [ "$ARM_HOSTED_BUILD" = yes ]; then - # The cross-tools' build process expects to find an existing cross toolchain - # under names like 'arm-apple-darwin$DARWIN_VERS-as'; so make them. - rm -rf $DIR/bin || exit 1 - mkdir $DIR/bin || exit 1 - for prog in ar nm ranlib strip lipo ld as ; do - P=$DIR/bin/arm-apple-darwin$DARWIN_VERS-${prog} - T=`xcrun -sdk $SDKROOT -find ${prog}` - ln -s $T $DIR/bin/$prog - echo '#!/bin/sh' > $P || exit 1 - echo 'exec '$T' "$@"' >> $P || exit 1 - chmod a+x $P || exit 1 - done - # Set up the links for clang. - for prog in clang clang++ ; do - P=$DIR/bin/arm-apple-darwin$DARWIN_VERS-${prog} - T=`xcrun -sdk $SDKROOT -find ${prog}` - ln -s $T $DIR/bin/$prog - echo '#!/bin/sh' > $P || exit 1 - echo 'exec '$T' -arch armv7 -isysroot '${SDKROOT_PATH}' "$@"' >> $P || exit 1 - chmod a+x $P || exit 1 - done - - PATH=$DIR/bin:$PATH - - unset SDKROOT && \ - $SRC_DIR/configure $COMMON_CONFIGURE_OPTS \ - --enable-targets=arm,arm64 \ - --host=arm-apple-darwin10 \ - --target=arm-apple-darwin10 \ - --build=i686-apple-darwin10 \ - --program-prefix="" \ - || exit 1 - - if [ -n "$IPHONEOS_DEPLOYMENT_TARGET" ]; then - COMMON_MAKEFLAGS="$COMMON_MAKEFLAGS \ - DEPLOYMENT_TARGET=-mios-version-min=$IPHONEOS_DEPLOYMENT_TARGET" - fi - - make $JOBS_FLAG $COMMON_MAKEFLAGS SDKROOT= UNIVERSAL_ARCH="$HOSTS" \ - CXXFLAGS="-DLLVM_VERSION_INFO='\" Apple Build #$LLVM_VERSION\"'" - if [ $? != 0 ] ; then - echo "error: LLVM 'make' failed!" - exit 1 - fi - -else -# not $ARM_HOSTED_BUILD - - if [ "$IOS_SIM_BUILD" = yes ]; then - export CC=`xcrun -sdk iphonesimulator -find clang` - export CXX=`xcrun -sdk iphonesimulator -find clang++` - - # Use a non-standard "darwin_sim" host triple to trigger a cross-build. - configure_opts="--enable-targets=x86 --host=i686-apple-darwin_sim \ - --build=i686-apple-darwin10" - if [ -n "$IPHONEOS_DEPLOYMENT_TARGET" ]; then - COMMON_MAKEFLAGS="$COMMON_MAKEFLAGS \ - DEPLOYMENT_TARGET=-mios-simulator-version-min=$IPHONEOS_DEPLOYMENT_TARGET" - fi - else - export CC=`xcrun -sdk macosx -find clang` - export CXX=`xcrun -sdk macosx -find clang++` - - configure_opts="--enable-targets=arm,arm64,x86" - if [ -n "$MACOSX_DEPLOYMENT_TARGET" ]; then - COMMON_MAKEFLAGS="$COMMON_MAKEFLAGS \ - DEPLOYMENT_TARGET=-mmacosx-version-min=$MACOSX_DEPLOYMENT_TARGET" - fi - fi - - if [ $SDKROOT_PATH ]; then - CPPFLAGS="$CPPFLAGS -isysroot $SDKROOT_PATH" - fi - for host in $HOSTS; do :; done - CPPFLAGS="$CPPFLAGS -arch $host" - - $SRC_DIR/configure $COMMON_CONFIGURE_OPTS $configure_opts \ - --program-prefix="" \ - CPPFLAGS="$CPPFLAGS" \ - || exit 1 - - make $JOBS_FLAG $COMMON_MAKEFLAGS UNIVERSAL_ARCH="$HOSTS" \ - CXXFLAGS="-DLLVM_VERSION_INFO='\" Apple Build #$LLVM_VERSION\"'" - if [ $? != 0 ] ; then - echo "error: LLVM 'make' failed!" - exit 1 - fi -fi - -################################################################################ -# Construct the actual destination root, by copying stuff from $DIR/dst-* to -# $DEST_DIR, with occasional 'lipo' commands. - -cd $DEST_DIR || exit 1 - -# Clean out DEST_DIR in case -noclean was passed to buildit. -rm -rf * || exit 1 - -cd $DIR/obj-llvm || exit 1 - -# Install the tree into the destination directory. -make $JOBS_FLAG $COMMON_MAKEFLAGS UNIVERSAL_ARCH="$HOSTS" install -if ! test $? == 0 ; then - echo "error: LLVM 'make install' failed!" - exit 1 -fi - -# Install Version.h -LLVM_MINOR_VERSION=`echo $LLVM_SUBMIT_SUBVERSION | sed -e 's,0*\([1-9][0-9]*\),\1,'` -if [ "x$LLVM_MINOR_VERSION" = "x" ]; then - LLVM_MINOR_VERSION=0 -fi -RC_ProjectSourceSubversion=`printf "%d" $LLVM_MINOR_VERSION` -echo "#define LLVM_VERSION ${RC_ProjectSourceVersion}" > $DEST_DIR$DEST_ROOT/include/llvm/Version.h -echo "#define LLVM_MINOR_VERSION ${RC_ProjectSourceSubversion}" >> $DEST_DIR$DEST_ROOT/include/llvm/Version.h - -# Run unifdef to preprocess the installed headers to reflect whether this -# was a debug or release build. -for file in `find $DEST_DIR$DEST_ROOT/include -type f -print`; do - if [ "$LLVM_ASSERTIONS" = yes ]; then - unifdef -UNDEBUG -D_DEBUG -o $file $file - else - unifdef -DNDEBUG -U_DEBUG -ULLVM_ENABLE_DUMP -o $file $file - fi -done - -# Find the right version of strip to use. -STRIP=strip -if [ -n "$SDKROOT" ]; then - STRIP=`xcrun -sdk $SDKROOT -find strip` -fi - -if [ "x$LLVM_DEBUG" != "x1" ]; then - # Strip local symbols from llvm libraries. - # - # Use '-l' to strip i386 modules. N.B. that flag doesn't work with kext or - # PPC objects! - $STRIP -Sl $DEST_DIR$DEST_ROOT/lib/*.[oa] - for f in `ls $DEST_DIR$DEST_ROOT/lib/*.so`; do - $STRIP -Sxl $f - done -fi - -# Remove .dir files -cd $DEST_DIR$DEST_ROOT -rm -f bin/.dir etc/llvm/.dir lib/.dir - -# The Hello dylib is an example of how to build a pass. -# The BugpointPasses module is only used to test bugpoint. -# These unversioned dylibs cause verification failures, so do not install them. -# (The wildcards are used to match a "lib" prefix if it is present.) -rm $DEST_DIR$DEST_ROOT/lib/*LLVMHello.dylib -rm $DEST_DIR$DEST_ROOT/lib/*BugpointPasses.dylib - -# Compress manpages -MDIR=$DEST_DIR$DEST_ROOT/share/man/man1 -gzip -f $MDIR/* - -################################################################################ -# Create SYM_DIR with information required for debugging. - -# Figure out how many make processes to run. -SYSCTL=`sysctl -n hw.activecpu` - -# hw.activecpu only available in 10.2.6 and later -if [ -z "$SYSCTL" ]; then - SYSCTL=`sysctl -n hw.ncpu` -fi - -# sysctl -n hw.* does not work when invoked via B&I chroot /BuildRoot. Builders -# can default to 2, since even if they are single processor, nothing else is -# running on the machine. -if [ -z "$SYSCTL" ]; then - SYSCTL=2 -fi - -cd $SYM_DIR || exit 1 - -# Clean out SYM_DIR in case -noclean was passed to buildit. -rm -rf * || exit 1 - -# Generate .dSYM files -DSYMUTIL=`xcrun -find dsymutil` -find $DEST_DIR -perm -0111 -type f \ - ! \( -name '*.la' -o -name gccas -o -name gccld -o -name llvm-config -o -name '*.a' \) \ - -print | xargs -n 1 -P ${SYSCTL} ${DSYMUTIL} - -# Save .dSYM files and .a archives -cd $DEST_DIR || exit 1 -find . \( -path \*.dSYM/\* -or -name \*.a \) -print \ - | cpio -pdml $SYM_DIR || exit 1 - -# Save source files. -mkdir $SYM_DIR/src || exit 1 -cd $DIR || exit 1 -find obj-* -name \*.\[chy\] -o -name \*.cpp -print \ - | cpio -pdml $SYM_DIR/src || exit 1 - -################################################################################ -# Remove libLTO.dylib and lto.h. Those are installed by clang. - -cd $DEST_DIR$DEST_ROOT -rm -f lib/libLTO.dylib -rm -f lib/libLTO.a lib/libLTO.la -find $DEST_DIR$DEST_ROOT -name lto.h -delete - -################################################################################ -# Remove debugging information from DEST_DIR. - -cd $DIR || exit 1 - -find $DEST_DIR -name \*.a -print | xargs ranlib || exit 1 -find $DEST_DIR -name \*.dSYM -print | xargs rm -r || exit 1 - -# Strip debugging information from files -# -# Use '-l' to strip i386 modules. N.B. that flag doesn't work with kext or -# PPC objects! -find $DEST_DIR -perm -0111 -type f \ - ! \( -name '*.la' -o -name gccas -o -name gccld -o -name llvm-config \) \ - -print | xargs -n 1 -P ${SYSCTL} $STRIP -arch all -Sl - -chgrp -h -R wheel $DEST_DIR -chgrp -R wheel $DEST_DIR - -################################################################################ -# Remove the docs directory - -rm -rf $DEST_DIR$DEST_ROOT/docs - -################################################################################ -# w00t! Done! - -exit 0 diff --git a/utils/count/Makefile b/utils/count/Makefile deleted file mode 100644 index 2a955e66679f0..0000000000000 --- a/utils/count/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -##===- utils/count/Makefile --------------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../.. -TOOLNAME = count -USEDLIBS = - -# This tool has no plugins, optimize startup time. -TOOL_NO_EXPORTS = 1 - -# FIXME: Don't install this utility -#NO_INSTALL = 1 - -include $(LEVEL)/Makefile.common diff --git a/utils/extract_symbols.py b/utils/extract_symbols.py new file mode 100755 index 0000000000000..9f467a7d0556b --- /dev/null +++ b/utils/extract_symbols.py @@ -0,0 +1,496 @@ +#!/usr/bin/env python + +"""A tool for extracting a list of symbols to export + +When exporting symbols from a dll or exe we either need to mark the symbols in +the source code as __declspec(dllexport) or supply a list of symbols to the +linker. This program automates the latter by inspecting the symbol tables of a +list of link inputs and deciding which of those symbols need to be exported. + +We can't just export all the defined symbols, as there's a limit of 65535 +exported symbols and in clang we go way over that, particularly in a debug +build. Therefore a large part of the work is pruning symbols either which can't +be imported, or which we think are things that have definitions in public header +files (i.e. template instantiations) and we would get defined in the thing +importing these symbols anyway. +""" + +from __future__ import print_function +import sys +import re +import os +import subprocess +import multiprocessing +import argparse + +# Define functions which extract a list of symbols from a library using several +# different tools. We use subprocess.Popen and yield a symbol at a time instead +# of using subprocess.check_output and returning a list as, especially on +# Windows, waiting for the entire output to be ready can take a significant +# amount of time. + +def dumpbin_get_symbols(lib): + process = subprocess.Popen(['dumpbin','/symbols',lib], bufsize=1, + stdout=subprocess.PIPE, stdin=subprocess.PIPE, + universal_newlines=True) + process.stdin.close() + for line in process.stdout: + # Look for external symbols that are defined in some section + match = re.match("^.+SECT.+External\s+\|\s+(\S+).*$", line) + if match: + yield match.group(1) + process.wait() + +def nm_get_symbols(lib): + process = subprocess.Popen(['nm',lib], bufsize=1, + stdout=subprocess.PIPE, stdin=subprocess.PIPE, + universal_newlines=True) + process.stdin.close() + for line in process.stdout: + # Look for external symbols that are defined in some section + match = re.match("^\S+\s+[BDGRSTVW]\s+(\S+)$", line) + if match: + yield match.group(1) + process.wait() + +def readobj_get_symbols(lib): + process = subprocess.Popen(['llvm-readobj','-symbols',lib], bufsize=1, + stdout=subprocess.PIPE, stdin=subprocess.PIPE, + universal_newlines=True) + process.stdin.close() + for line in process.stdout: + # When looking through the output of llvm-readobj we expect to see Name, + # Section, then StorageClass, so record Name and Section when we see + # them and decide if this is a defined external symbol when we see + # StorageClass. + match = re.search('Name: (\S+)', line) + if match: + name = match.group(1) + match = re.search('Section: (\S+)', line) + if match: + section = match.group(1) + match = re.search('StorageClass: (\S+)', line) + if match: + storageclass = match.group(1) + if section != 'IMAGE_SYM_ABSOLUTE' and \ + section != 'IMAGE_SYM_UNDEFINED' and \ + storageclass == 'External': + yield name + process.wait() + +# Define functions which determine if the target is 32-bit Windows (as that's +# where calling convention name decoration happens). + +def dumpbin_is_32bit_windows(lib): + # dumpbin /headers can output a huge amount of data (>100MB in a debug + # build) so we read only up to the 'machine' line then close the output. + process = subprocess.Popen(['dumpbin','/headers',lib], bufsize=1, + stdout=subprocess.PIPE, stdin=subprocess.PIPE, + universal_newlines=True) + process.stdin.close() + retval = False + for line in process.stdout: + match = re.match('.+machine \((\S+)\)', line) + if match: + retval = (match.group(1) == 'x86') + break + process.stdout.close() + process.wait() + return retval + +def objdump_is_32bit_windows(lib): + output = subprocess.check_output(['objdump','-f',lib], + universal_newlines=True) + for line in output: + match = re.match('.+file format (\S+)', line) + if match: + return (match.group(1) == 'pe-i386') + return False + +def readobj_is_32bit_windows(lib): + output = subprocess.check_output(['llvm-readobj','-file-headers',lib], + universal_newlines=True) + for line in output: + match = re.match('Format: (\S+)', line) + if match: + return (match.group(1) == 'COFF-i386') + return False + +# MSVC mangles names to ?<identifier_mangling>@<type_mangling>. By examining the +# identifier/type mangling we can decide which symbols could possibly be +# required and which we can discard. +def should_keep_microsoft_symbol(symbol, calling_convention_decoration): + # Keep unmangled (i.e. extern "C") names + if not '?' in symbol: + if calling_convention_decoration: + # Remove calling convention decoration from names + match = re.match('[_@]([^@]+)', symbol) + if match: + return match.group(1) + return symbol + # Function template instantiations start with ?$, discard them as it's + # assumed that the definition is public + elif symbol.startswith('??$'): + return None + # Deleting destructors start with ?_G or ?_E and can be discarded because + # link.exe gives you a warning telling you they can't be exported if you + # don't + elif symbol.startswith('??_G') or symbol.startswith('??_E'): + return None + # Constructors (?0) and destructors (?1) of templates (?$) are assumed to be + # defined in headers and not required to be kept + elif symbol.startswith('??0?$') or symbol.startswith('??1?$'): + return None + # An anonymous namespace is mangled as ?A(maybe hex number)@. Any symbol + # that mentions an anonymous namespace can be discarded, as the anonymous + # namespace doesn't exist outside of that translation unit. + elif re.search('\?A(0x\w+)?@', symbol): + return None + # Keep mangled llvm:: and clang:: function symbols. How we detect these is a + # bit of a mess and imprecise, but that avoids having to completely demangle + # the symbol name. The outermost namespace is at the end of the identifier + # mangling, and the identifier mangling is followed by the type mangling, so + # we look for (llvm|clang)@@ followed by something that looks like a + # function type mangling. To spot a function type we use (this is derived + # from clang/lib/AST/MicrosoftMangle.cpp): + # <function-type> ::= <function-class> <this-cvr-qualifiers> + # <calling-convention> <return-type> + # <argument-list> <throw-spec> + # <function-class> ::= [A-Z] + # <this-cvr-qualifiers> ::= [A-Z0-9_]* + # <calling-convention> ::= [A-JQ] + # <return-type> ::= .+ + # <argument-list> ::= X (void) + # ::= .+@ (list of types) + # ::= .*Z (list of types, varargs) + # <throw-spec> ::= exceptions are not allowed + elif re.search('(llvm|clang)@@[A-Z][A-Z0-9_]*[A-JQ].+(X|.+@|.*Z)$', symbol): + return symbol + return None + +# Itanium manglings are of the form _Z<identifier_mangling><type_mangling>. We +# demangle the identifier mangling to identify symbols that can be safely +# discarded. +def should_keep_itanium_symbol(symbol, calling_convention_decoration): + # Start by removing any calling convention decoration (which we expect to + # see on all symbols, even mangled C++ symbols) + if calling_convention_decoration and symbol.startswith('_'): + symbol = symbol[1:] + # Keep unmangled names + if not symbol.startswith('_') and not symbol.startswith('.'): + return symbol + # Discard manglings that aren't nested names + match = re.match('_Z(T[VTIS])?(N.+)', symbol) + if not match: + return None + # Demangle the name. If the name is too complex then we don't need to keep + # it, but it the demangling fails then keep the symbol just in case. + try: + names, _ = parse_itanium_nested_name(match.group(2)) + except TooComplexName: + return None + if not names: + return symbol + # Constructors and destructors of templates classes are assumed to be + # defined in headers and not required to be kept + if re.match('[CD][123]', names[-1][0]) and names[-2][1]: + return None + # Discard function template instantiations as it's assumed that the + # definition is public + elif names[-1][1]: + return None + # Keep llvm:: and clang:: names + elif names[0][0] == '4llvm' or names[0][0] == '5clang': + return symbol + # Discard everything else + else: + return None + +# Certain kinds of complex manglings we assume cannot be part of a public +# interface, and we handle them by raising an exception. +class TooComplexName(Exception): + pass + +# Parse an itanium mangled name from the start of a string and return a +# (name, rest of string) pair. +def parse_itanium_name(arg): + # Check for a normal name + match = re.match('(\d+)(.+)', arg) + if match: + n = int(match.group(1)) + name = match.group(1)+match.group(2)[:n] + rest = match.group(2)[n:] + return name, rest + # Check for constructor/destructor names + match = re.match('([CD][123])(.+)', arg) + if match: + return match.group(1), match.group(2) + # Assume that a sequence of characters that doesn't end a nesting is an + # operator (this is very imprecise, but appears to be good enough) + match = re.match('([^E]+)(.+)', arg) + if match: + return match.group(1), match.group(2) + # Anything else: we can't handle it + return None, arg + +# Parse an itanium mangled template argument list from the start of a string +# and throw it away, returning the rest of the string. +def skip_itanium_template(arg): + # A template argument list starts with I + assert arg.startswith('I'), arg + tmp = arg[1:] + while tmp: + # Check for names + match = re.match('(\d+)(.+)', tmp) + if match: + n = int(match.group(1)) + tmp = match.group(2)[n:] + continue + # Check for substitutions + match = re.match('S[A-Z0-9]*_(.+)', tmp) + if match: + tmp = match.group(1) + # Start of a template + elif tmp.startswith('I'): + tmp = skip_itanium_template(tmp) + # Start of a nested name + elif tmp.startswith('N'): + _, tmp = parse_itanium_nested_name(tmp) + # Start of an expression: assume that it's too complicated + elif tmp.startswith('L') or tmp.startswith('X'): + raise TooComplexName + # End of the template + elif tmp.startswith('E'): + return tmp[1:] + # Something else: probably a type, skip it + else: + tmp = tmp[1:] + return None + +# Parse an itanium mangled nested name and transform it into a list of pairs of +# (name, is_template), returning (list, rest of string). +def parse_itanium_nested_name(arg): + # A nested name starts with N + assert arg.startswith('N'), arg + ret = [] + + # Skip past the N, and possibly a substitution + match = re.match('NS[A-Z0-9]*_(.+)', arg) + if match: + tmp = match.group(1) + else: + tmp = arg[1:] + + # Skip past CV-qualifiers and ref qualifiers + match = re.match('[rVKRO]*(.+)', tmp); + if match: + tmp = match.group(1) + + # Repeatedly parse names from the string until we reach the end of the + # nested name + while tmp: + # An E ends the nested name + if tmp.startswith('E'): + return ret, tmp[1:] + # Parse a name + name_part, tmp = parse_itanium_name(tmp) + if not name_part: + # If we failed then we don't know how to demangle this + return None, None + is_template = False + # If this name is a template record that, then skip the template + # arguments + if tmp.startswith('I'): + tmp = skip_itanium_template(tmp) + is_template = True + # Add the name to the list + ret.append((name_part, is_template)) + + # If we get here then something went wrong + return None, None + +def extract_symbols(arg): + get_symbols, should_keep_symbol, calling_convention_decoration, lib = arg + symbols = dict() + for symbol in get_symbols(lib): + symbol = should_keep_symbol(symbol, calling_convention_decoration) + if symbol: + symbols[symbol] = 1 + symbols.setdefault(symbol,0) + return symbols + +if __name__ == '__main__': + tool_exes = ['dumpbin','nm','objdump','llvm-readobj'] + parser = argparse.ArgumentParser( + description='Extract symbols to export from libraries') + parser.add_argument('--mangling', choices=['itanium','microsoft'], + required=True, help='expected symbol mangling scheme') + parser.add_argument('--tools', choices=tool_exes, nargs='*', + help='tools to use to extract symbols and determine the' + ' target') + parser.add_argument('libs', metavar='lib', type=str, nargs='+', + help='libraries to extract symbols from') + parser.add_argument('-o', metavar='file', type=str, help='output to file') + args = parser.parse_args() + + # Determine the function to use to get the list of symbols from the inputs, + # and the function to use to determine if the target is 32-bit windows. + tools = { 'dumpbin' : (dumpbin_get_symbols, dumpbin_is_32bit_windows), + 'nm' : (nm_get_symbols, None), + 'objdump' : (None, objdump_is_32bit_windows), + 'llvm-readobj' : (readobj_get_symbols, readobj_is_32bit_windows) } + get_symbols = None + is_32bit_windows = None + # If we have a tools argument then use that for the list of tools to check + if args.tools: + tool_exes = args.tools + # Find a tool to use by trying each in turn until we find one that exists + # (subprocess.call will throw OSError when the program does not exist) + get_symbols = None + for exe in tool_exes: + try: + # Close std streams as we don't want any output and we don't + # want the process to wait for something on stdin. + p = subprocess.Popen([exe], stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + stdin=subprocess.PIPE, + universal_newlines=True) + p.stdout.close() + p.stderr.close() + p.stdin.close() + p.wait() + # Keep going until we have a tool to use for both get_symbols and + # is_32bit_windows + if not get_symbols: + get_symbols = tools[exe][0] + if not is_32bit_windows: + is_32bit_windows = tools[exe][1] + if get_symbols and is_32bit_windows: + break + except OSError: + continue + if not get_symbols: + print("Couldn't find a program to read symbols with", file=sys.stderr) + exit(1) + if not is_32bit_windows: + print("Couldn't find a program to determing the target", file=sys.stderr) + exit(1) + + # How we determine which symbols to keep and which to discard depends on + # the mangling scheme + if args.mangling == 'microsoft': + should_keep_symbol = should_keep_microsoft_symbol + else: + should_keep_symbol = should_keep_itanium_symbol + + # Get the list of libraries to extract symbols from + libs = list() + for lib in args.libs: + # When invoked by cmake the arguments are the cmake target names of the + # libraries, so we need to add .lib/.a to the end and maybe lib to the + # start to get the filename. Also allow objects. + suffixes = ['.lib','.a','.obj','.o'] + if not any([lib.endswith(s) for s in suffixes]): + for s in suffixes: + if os.path.exists(lib+s): + lib = lib+s + break + if os.path.exists('lib'+lib+s): + lib = 'lib'+lib+s + break + if not any([lib.endswith(s) for s in suffixes]): + print("Don't know what to do with argument "+lib, file=sys.stderr) + exit(1) + libs.append(lib) + + # Check if calling convention decoration is used by inspecting the first + # library in the list + calling_convention_decoration = is_32bit_windows(libs[0]) + + # Extract symbols from libraries in parallel. This is a huge time saver when + # doing a debug build, as there are hundreds of thousands of symbols in each + # library. + pool = multiprocessing.Pool() + try: + # Only one argument can be passed to the mapping function, and we can't + # use a lambda or local function definition as that doesn't work on + # windows, so create a list of tuples which duplicates the arguments + # that are the same in all calls. + vals = [(get_symbols, should_keep_symbol, calling_convention_decoration, x) for x in libs] + # Do an async map then wait for the result to make sure that + # KeyboardInterrupt gets caught correctly (see + # http://bugs.python.org/issue8296) + result = pool.map_async(extract_symbols, vals) + pool.close() + libs_symbols = result.get(3600) + except KeyboardInterrupt: + # On Ctrl-C terminate everything and exit + pool.terminate() + pool.join() + exit(1) + + # Merge everything into a single dict + symbols = dict() + for this_lib_symbols in libs_symbols: + for k,v in list(this_lib_symbols.items()): + symbols[k] = v + symbols.setdefault(k,0) + + # Count instances of member functions of template classes, and map the + # symbol name to the function+class. We do this under the assumption that if + # a member function of a template class is instantiated many times it's + # probably declared in a public header file. + template_function_count = dict() + template_function_mapping = dict() + template_function_count[""] = 0 + for k in symbols: + name = None + if args.mangling == 'microsoft': + # Member functions of templates start with + # ?<fn_name>@?$<class_name>@, so we map to <fn_name>@?$<class_name>. + # As manglings go from the innermost scope to the outermost scope + # this means: + # * When we have a function member of a subclass of a template + # class then <fn_name> will actually contain the mangling of + # both the subclass and the function member. This is fine. + # * When we have a function member of a template subclass of a + # (possibly template) class then it's the innermost template + # subclass that becomes <class_name>. This should be OK so long + # as we don't have multiple classes with a template subclass of + # the same name. + match = re.search("^\?(\??\w+\@\?\$\w+)\@", k) + if match: + name = match.group(1) + else: + # Find member functions of templates by demangling the name and + # checking if the second-to-last name in the list is a template. + match = re.match('_Z(T[VTIS])?(N.+)', k) + if match: + try: + names, _ = parse_itanium_nested_name(match.group(2)) + if names and names[-2][1]: + name = ''.join([x for x,_ in names]) + except TooComplexName: + # Manglings that are too complex should already have been + # filtered out, but if we happen to somehow see one here + # just leave it as-is. + pass + if name: + old_count = template_function_count.setdefault(name,0) + template_function_count[name] = old_count + 1 + template_function_mapping[k] = name + else: + template_function_mapping[k] = "" + + # Print symbols which both: + # * Appear in exactly one input, as symbols defined in multiple + # objects/libraries are assumed to have public definitions. + # * Aren't instances of member functions of templates which have been + # instantiated 100 times or more, which are assumed to have public + # definitions. (100 is an arbitrary guess here.) + if args.o: + outfile = open(args.o,'w') + else: + outfile = sys.stdout + for k,v in list(symbols.items()): + template_count = template_function_count[template_function_mapping[k]] + if v == 1 and template_count < 100: + print(k, file=outfile) diff --git a/utils/findoptdiff b/utils/findoptdiff index 7a2eab05d71a5..9a8803184384e 100755 --- a/utils/findoptdiff +++ b/utils/findoptdiff @@ -70,7 +70,7 @@ dis2="$llvm2/Debug/bin/llvm-dis" opt1="$llvm1/Debug/bin/opt" opt2="$llvm2/Debug/bin/opt" -all_switches="-verify -lowersetjmp -simplifycfg -mem2reg -globalopt -globaldce -ipconstprop -deadargelim -instcombine -simplifycfg -prune-eh -inline -simplify-libcalls -argpromotion -tailduplicate -simplifycfg -scalarrepl -instcombine -predsimplify -condprop -tailcallelim -simplifycfg -reassociate -licm -loop-unswitch -instcombine -indvars -loop-unroll -instcombine -load-vn -gcse -sccp -instcombine -condprop -dse -dce -simplifycfg -deadtypeelim -constmerge -internalize -ipsccp -globalopt -constmerge -deadargelim -inline -prune-eh -globalopt -globaldce -argpromotion -instcombine -predsimplify -scalarrepl -globalsmodref-aa -licm -load-vn -gcse -dse -instcombine -simplifycfg -verify" +all_switches="-verify -lowersetjmp -simplifycfg -mem2reg -globalopt -globaldce -ipconstprop -deadargelim -instcombine -simplifycfg -prune-eh -inline -simplify-libcalls -argpromotion -tailduplicate -simplifycfg -sroa -instcombine -predsimplify -condprop -tailcallelim -simplifycfg -reassociate -licm -loop-unswitch -instcombine -indvars -loop-unroll -instcombine -load-vn -gcse -sccp -instcombine -condprop -dse -dce -simplifycfg -deadtypeelim -constmerge -internalize -ipsccp -globalopt -constmerge -deadargelim -inline -prune-eh -globalopt -globaldce -argpromotion -instcombine -predsimplify -sroa -globalsmodref-aa -licm -load-vn -gcse -dse -instcombine -simplifycfg -verify" #counter=0 function tryit { diff --git a/utils/fpcmp/Makefile b/utils/fpcmp/Makefile deleted file mode 100644 index 81db3b9c3f6ee..0000000000000 --- a/utils/fpcmp/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -##===- utils/fpcmp/Makefile --------------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../.. -TOOLNAME = fpcmp -USEDLIBS = LLVMSupport.a -NO_INSTALL = 1 - -include $(LEVEL)/Makefile.common - diff --git a/utils/gdb-scripts/prettyprinters.py b/utils/gdb-scripts/prettyprinters.py new file mode 100644 index 0000000000000..0dbc7af3e46ef --- /dev/null +++ b/utils/gdb-scripts/prettyprinters.py @@ -0,0 +1,107 @@ +import gdb.printing +class SmallStringPrinter: + """Print an llvm::SmallString object.""" + + def __init__(self, val): + self.val = val + + def to_string(self): + begin = self.val['BeginX'] + end = self.val['EndX'] + return begin.cast(gdb.lookup_type("char").pointer()).string(length = end - begin) + + def display_hint (self): + return 'string' + +class StringRefPrinter: + """Print an llvm::StringRef object.""" + + def __init__(self, val): + self.val = val + + def to_string(self): + return self.val['Data'].string(length = self.val['Length']) + + def display_hint (self): + return 'string' + +class SmallVectorPrinter: + """Print an llvm::SmallVector object.""" + + class _iterator: + def __init__(self, begin, end): + self.cur = begin + self.end = end + self.count = 0 + + def __iter__(self): + return self + + def next(self): + if self.cur == self.end: + raise StopIteration + count = self.count + self.count = self.count + 1 + cur = self.cur + self.cur = self.cur + 1 + return '[%d]' % count, cur.dereference() + + def __init__(self, val): + self.val = val + + def children(self): + t = self.val.type.template_argument(0).pointer() + begin = self.val['BeginX'].cast(t) + end = self.val['EndX'].cast(t) + return self._iterator(begin, end) + + def to_string(self): + t = self.val.type.template_argument(0).pointer() + begin = self.val['BeginX'].cast(t) + end = self.val['EndX'].cast(t) + capacity = self.val['CapacityX'].cast(t) + return 'llvm::SmallVector of length %d, capacity %d' % (end - begin, capacity - begin) + + def display_hint (self): + return 'array' + +class ArrayRefPrinter: + """Print an llvm::ArrayRef object.""" + + class _iterator: + def __init__(self, begin, end): + self.cur = begin + self.end = end + self.count = 0 + + def __iter__(self): + return self + + def next(self): + if self.cur == self.end: + raise StopIteration + count = self.count + self.count = self.count + 1 + cur = self.cur + self.cur = self.cur + 1 + return '[%d]' % count, cur.dereference() + + def __init__(self, val): + self.val = val + + def children(self): + data = self.val['Data'] + return self._iterator(data, data + self.val['Length']) + + def to_string(self): + return 'llvm::ArrayRef of length %d' % (self.val['Length']) + + def display_hint (self): + return 'array' + +pp = gdb.printing.RegexpCollectionPrettyPrinter("LLVMSupport") +pp.add_printer('llvm::SmallString', '^llvm::SmallString<.*>$', SmallStringPrinter) +pp.add_printer('llvm::StringRef', '^llvm::StringRef$', StringRefPrinter) +pp.add_printer('llvm::SmallVectorImpl', '^llvm::SmallVector(Impl)?<.*>$', SmallVectorPrinter) +pp.add_printer('llvm::ArrayRef', '^llvm::(Const)?ArrayRef<.*>$', ArrayRefPrinter) +gdb.printing.register_pretty_printer(gdb.current_objfile(), pp) diff --git a/utils/lit/lit/Test.py b/utils/lit/lit/Test.py index ef0e7bfc2a37d..9c9c47d63217a 100644 --- a/utils/lit/lit/Test.py +++ b/utils/lit/lit/Test.py @@ -102,11 +102,18 @@ class JSONMetricValue(MetricValue): def toMetricValue(value): if isinstance(value, MetricValue): return value - elif isinstance(value, int) or isinstance(value, long): + elif isinstance(value, int): return IntMetricValue(value) elif isinstance(value, float): return RealMetricValue(value) else: + # 'long' is only present in python2 + try: + if isinstance(value, long): + return IntMetricValue(value) + except NameError: + pass + # Try to create a JSONMetricValue and let the constructor throw # if value is not a valid type. return JSONMetricValue(value) @@ -230,11 +237,20 @@ class Test: return True # If this is a part of the target triple, it fails. - if item in self.suite.config.target_triple: + if item and item in self.suite.config.target_triple: return True return False + def isEarlyTest(self): + """ + isEarlyTest() -> bool + + Check whether this test should be executed early in a particular run. + This can be used for test suites with long running tests to maximize + parallelism or where it is desirable to surface their failures early. + """ + return self.suite.config.is_early def getJUnitXML(self): test_name = self.path_in_suite[-1] diff --git a/utils/lit/lit/TestRunner.py b/utils/lit/lit/TestRunner.py index 1af82e1584456..24d687280c43f 100644 --- a/utils/lit/lit/TestRunner.py +++ b/utils/lit/lit/TestRunner.py @@ -110,6 +110,18 @@ class TimeoutHelper(object): self._procs = [] # Python2 doesn't have list.clear() self._doneKillPass = True +class ShellCommandResult(object): + """Captures the result of an individual command.""" + + def __init__(self, command, stdout, stderr, exitCode, timeoutReached, + outputFiles = []): + self.command = command + self.stdout = stdout + self.stderr = stderr + self.exitCode = exitCode + self.timeoutReached = timeoutReached + self.outputFiles = list(outputFiles) + def executeShCmd(cmd, shenv, results, timeout=0): """ Wrapper around _executeShCmd that handles @@ -243,8 +255,13 @@ def _executeShCmd(cmd, shenv, results, timeoutHelper): result = subprocess.PIPE else: if r[2] is None: + redir_filename = None if kAvoidDevNull and r[0] == '/dev/null': r[2] = tempfile.TemporaryFile(mode=r[1]) + elif kIsWindows and r[0] == '/dev/tty': + # Simulate /dev/tty on Windows. + # "CON" is a special filename for the console. + r[2] = open("CON", r[1]) else: # Make sure relative paths are relative to the cwd. redir_filename = os.path.join(cmd_shenv.cwd, r[0]) @@ -254,7 +271,7 @@ def _executeShCmd(cmd, shenv, results, timeoutHelper): # FIXME: Actually, this is probably an instance of PR6753. if r[1] == 'a': r[2].seek(0, 2) - opened_files.append(r[2]) + opened_files.append(tuple(r) + (redir_filename,)) result = r[2] final_redirects.append(result) @@ -328,7 +345,7 @@ def _executeShCmd(cmd, shenv, results, timeoutHelper): # need to release any handles we may have on the temporary files (important # on Win32, for example). Since we have already spawned the subprocess, our # handles have already been transferred so we do not need them anymore. - for f in opened_files: + for (name, mode, f, path) in opened_files: f.close() # FIXME: There is probably still deadlock potential here. Yawn. @@ -365,15 +382,36 @@ def _executeShCmd(cmd, shenv, results, timeoutHelper): # Ensure the resulting output is always of string type. try: - out = to_string(out.decode('utf-8')) + if out is None: + out = '' + else: + out = to_string(out.decode('utf-8', errors='replace')) except: out = str(out) try: - err = to_string(err.decode('utf-8')) + if err is None: + err = '' + else: + err = to_string(err.decode('utf-8', errors='replace')) except: err = str(err) - results.append((cmd.commands[i], out, err, res, timeoutHelper.timeoutReached())) + # Gather the redirected output files for failed commands. + output_files = [] + if res != 0: + for (name, mode, f, path) in sorted(opened_files): + if path is not None and mode in ('w', 'a'): + try: + with open(path, 'rb') as f: + data = f.read() + except: + data = None + if data != None: + output_files.append((name, path, data)) + + results.append(ShellCommandResult( + cmd.commands[i], out, err, res, timeoutHelper.timeoutReached(), + output_files)) if cmd.pipe_err: # Python treats the exit code as a signed char. if exitCode is None: @@ -418,16 +456,49 @@ def executeScriptInternal(test, litConfig, tmpBase, commands, cwd): except InternalShellError: e = sys.exc_info()[1] exitCode = 127 - results.append((e.command, '', e.message, exitCode, False)) + results.append( + ShellCommandResult(e.command, '', e.message, exitCode, False)) out = err = '' - for i,(cmd, cmd_out, cmd_err, res, timeoutReached) in enumerate(results): - out += 'Command %d: %s\n' % (i, ' '.join('"%s"' % s for s in cmd.args)) - out += 'Command %d Result: %r\n' % (i, res) + for i,result in enumerate(results): + # Write the command line run. + out += '$ %s\n' % (' '.join('"%s"' % s + for s in result.command.args),) + + # If nothing interesting happened, move on. + if litConfig.maxIndividualTestTime == 0 and \ + result.exitCode == 0 and \ + not result.stdout.strip() and not result.stderr.strip(): + continue + + # Otherwise, something failed or was printed, show it. + + # Add the command output, if redirected. + for (name, path, data) in result.outputFiles: + if data.strip(): + out += "# redirected output from %r:\n" % (name,) + data = to_string(data.decode('utf-8', errors='replace')) + if len(data) > 1024: + out += data[:1024] + "\n...\n" + out += "note: data was truncated\n" + else: + out += data + out += "\n" + + if result.stdout.strip(): + out += '# command output:\n%s\n' % (result.stdout,) + if result.stderr.strip(): + out += '# command stderr:\n%s\n' % (result.stderr,) + if not result.stdout.strip() and not result.stderr.strip(): + out += "note: command had no output on stdout or stderr\n" + + # Show the error conditions: + if result.exitCode != 0: + out += "error: command failed with exit status: %d\n" % ( + result.exitCode,) if litConfig.maxIndividualTestTime > 0: - out += 'Command %d Reached Timeout: %s\n\n' % (i, str(timeoutReached)) - out += 'Command %d Output:\n%s\n\n' % (i, cmd_out) - out += 'Command %d Stderr:\n%s\n\n' % (i, cmd_err) + out += 'error: command reached timeout: %s\n' % ( + i, str(result.timeoutReached)) return out, err, exitCode, timeoutInfo @@ -564,6 +635,24 @@ def getDefaultSubstitutions(test, tmpDir, tmpBase, normalize_slashes=False): ('%/t', tmpBase.replace('\\', '/') + '.tmp'), ('%/T', tmpDir.replace('\\', '/')), ]) + + # "%:[STpst]" are paths without colons. + if kIsWindows: + substitutions.extend([ + ('%:s', re.sub(r'^(.):', r'\1', sourcepath)), + ('%:S', re.sub(r'^(.):', r'\1', sourcedir)), + ('%:p', re.sub(r'^(.):', r'\1', sourcedir)), + ('%:t', re.sub(r'^(.):', r'\1', tmpBase) + '.tmp'), + ('%:T', re.sub(r'^(.):', r'\1', tmpDir)), + ]) + else: + substitutions.extend([ + ('%:s', sourcepath), + ('%:S', sourcedir), + ('%:p', sourcedir), + ('%:t', tmpBase + '.tmp'), + ('%:T', tmpDir), + ]) return substitutions def applySubstitutions(script, substitutions): @@ -594,8 +683,10 @@ def parseIntegratedTestScript(test, require_script=True): sourcepath = test.getSourcePath() script = [] requires = [] + requires_any = [] unsupported = [] - keywords = ['RUN:', 'XFAIL:', 'REQUIRES:', 'UNSUPPORTED:', 'END.'] + keywords = ['RUN:', 'XFAIL:', 'REQUIRES:', 'REQUIRES-ANY:', + 'UNSUPPORTED:', 'END.'] for line_number, command_type, ln in \ parseIntegratedTestScriptCommands(sourcepath, keywords): if command_type == 'RUN': @@ -620,6 +711,8 @@ def parseIntegratedTestScript(test, require_script=True): test.xfails.extend([s.strip() for s in ln.split(',')]) elif command_type == 'REQUIRES': requires.extend([s.strip() for s in ln.split(',')]) + elif command_type == 'REQUIRES-ANY': + requires_any.extend([s.strip() for s in ln.split(',')]) elif command_type == 'UNSUPPORTED': unsupported.extend([s.strip() for s in ln.split(',')]) elif command_type == 'END': @@ -646,6 +739,12 @@ def parseIntegratedTestScript(test, require_script=True): msg = ', '.join(missing_required_features) return lit.Test.Result(Test.UNSUPPORTED, "Test requires the following features: %s" % msg) + requires_any_features = [f for f in requires_any + if f in test.config.available_features] + if requires_any and not requires_any_features: + msg = ' ,'.join(requires_any) + return lit.Test.Result(Test.UNSUPPORTED, + "Test requires any of the following features: %s" % msg) unsupported_features = [f for f in unsupported if f in test.config.available_features] if unsupported_features: diff --git a/utils/lit/lit/TestingConfig.py b/utils/lit/lit/TestingConfig.py index 7441b94b9f087..1e39a000c90d8 100644 --- a/utils/lit/lit/TestingConfig.py +++ b/utils/lit/lit/TestingConfig.py @@ -24,7 +24,8 @@ class TestingConfig: pass_vars = ['LIBRARY_PATH', 'LD_LIBRARY_PATH', 'SYSTEMROOT', 'TERM', 'LD_PRELOAD', 'ASAN_OPTIONS', 'UBSAN_OPTIONS', - 'LSAN_OPTIONS', 'ADB', 'ANDROID_SERIAL'] + 'LSAN_OPTIONS', 'ADB', 'ANDROID_SERIAL', + 'SANITIZER_IGNORE_CVE_2016_2143'] for var in pass_vars: val = os.environ.get(var, '') # Check for empty string as some variables such as LD_PRELOAD cannot be empty @@ -118,7 +119,8 @@ class TestingConfig: def __init__(self, parent, name, suffixes, test_format, environment, substitutions, unsupported, test_exec_root, test_source_root, excludes, - available_features, pipefail, limit_to_features = []): + available_features, pipefail, limit_to_features = [], + is_early = False): self.parent = parent self.name = str(name) self.suffixes = set(suffixes) @@ -135,6 +137,8 @@ class TestingConfig: # require one of the features in this list if this list is non-empty. # Configurations can set this list to restrict the set of tests to run. self.limit_to_features = set(limit_to_features) + # Whether the suite should be tested early in a given run. + self.is_early = bool(is_early) def finish(self, litConfig): """finish() - Finish this config object, after loading is complete.""" diff --git a/utils/lit/lit/formats/googletest.py b/utils/lit/lit/formats/googletest.py index 5b19d4e638f94..f0250a3f96dd1 100644 --- a/utils/lit/lit/formats/googletest.py +++ b/utils/lit/lit/formats/googletest.py @@ -43,6 +43,12 @@ class GoogleTest(TestFormat): if not ln.strip(): continue + if 'Running main() from gtest_main.cc' in ln: + # Upstream googletest prints this to stdout prior to running + # tests. LLVM removed that print statement in r61540, but we + # handle it here in case upstream googletest is being used. + continue + prefix = '' index = 0 while ln[index*2:index*2+2] == ' ': diff --git a/utils/lit/lit/main.py b/utils/lit/lit/main.py index 4df2571da998b..50927e2cbf022 100755 --- a/utils/lit/lit/main.py +++ b/utils/lit/lit/main.py @@ -336,6 +336,9 @@ def main(builtinParameters = {}): print(' %s - %d tests' %(ts.name, len(ts_tests))) print(' Source Root: %s' % ts.source_root) print(' Exec Root : %s' % ts.exec_root) + if ts.config.available_features: + print(' Available Features : %s' % ' '.join( + sorted(ts.config.available_features))) # Show the tests, if requested. if opts.showTests: @@ -367,7 +370,7 @@ def main(builtinParameters = {}): elif opts.incremental: sort_by_incremental_cache(run) else: - run.tests.sort(key = lambda result_test: result_test.getFullName()) + run.tests.sort(key = lambda t: (not t.isEarlyTest(), t.getFullName())) # Finally limit the number of tests, if desired. if opts.maxTests is not None: diff --git a/utils/lit/lit/run.py b/utils/lit/lit/run.py index 27c414d6dd65e..bbf644ef7dbeb 100644 --- a/utils/lit/lit/run.py +++ b/utils/lit/lit/run.py @@ -44,11 +44,13 @@ class LockedValue(object): value = property(_get_value, _set_value) class TestProvider(object): - def __init__(self, tests, num_jobs, queue_impl, canceled_flag): + def __init__(self, queue_impl, canceled_flag): self.canceled_flag = canceled_flag # Create a shared queue to provide the test indices. self.queue = queue_impl() + + def queue_tests(self, tests, num_jobs): for i in range(len(tests)): self.queue.put(i) for i in range(num_jobs): @@ -229,7 +231,15 @@ class Run(object): consumer = ThreadResultsConsumer(display) # Create the test provider. - provider = TestProvider(self.tests, jobs, queue_impl, canceled_flag) + provider = TestProvider(queue_impl, canceled_flag) + + # Queue the tests outside the main thread because we can't guarantee + # that we can put() all the tests without blocking: + # https://docs.python.org/2/library/multiprocessing.html + # e.g: On Mac OS X, we will hang if we put 2^15 elements in the queue + # without taking any out. + queuer = task_impl(target=provider.queue_tests, args=(self.tests, jobs)) + queuer.start() # Install a console-control signal handler on Windows. if win32api is not None: @@ -252,6 +262,8 @@ class Run(object): # Otherwise, execute the tests in parallel self._execute_tests_in_parallel(task_impl, provider, consumer, jobs) + queuer.join() + # Cancel the timeout handler. if max_time is not None: timeout_timer.cancel() diff --git a/utils/lit/tests/Inputs/googletest-upstream-format/DummySubDir/OneTest b/utils/lit/tests/Inputs/googletest-upstream-format/DummySubDir/OneTest new file mode 100755 index 0000000000000..d7bc5968f2611 --- /dev/null +++ b/utils/lit/tests/Inputs/googletest-upstream-format/DummySubDir/OneTest @@ -0,0 +1,38 @@ +#!/usr/bin/env python + +import sys + +if len(sys.argv) != 2: + raise ValueError("unexpected number of args") + +if sys.argv[1] == "--gtest_list_tests": + print("""\ +Running main() from gtest_main.cc +FirstTest. + subTestA + subTestB +ParameterizedTest/0. + subTest +ParameterizedTest/1. + subTest""") + sys.exit(0) +elif not sys.argv[1].startswith("--gtest_filter="): + raise ValueError("unexpected argument: %r" % (sys.argv[1])) + +test_name = sys.argv[1].split('=',1)[1] +print('Running main() from gtest_main.cc') +if test_name == 'FirstTest.subTestA': + print('I am subTest A, I PASS') + print('[ PASSED ] 1 test.') + sys.exit(0) +elif test_name == 'FirstTest.subTestB': + print('I am subTest B, I FAIL') + print('And I have two lines of output') + sys.exit(1) +elif test_name in ('ParameterizedTest/0.subTest', + 'ParameterizedTest/1.subTest'): + print('I am a parameterized test, I also PASS') + print('[ PASSED ] 1 test.') + sys.exit(0) +else: + raise SystemExit("error: invalid test name: %r" % (test_name,)) diff --git a/utils/lit/tests/Inputs/googletest-upstream-format/lit.cfg b/utils/lit/tests/Inputs/googletest-upstream-format/lit.cfg new file mode 100644 index 0000000000000..9fb5d2b0247be --- /dev/null +++ b/utils/lit/tests/Inputs/googletest-upstream-format/lit.cfg @@ -0,0 +1,3 @@ +import lit.formats +config.name = 'googletest-upstream-format' +config.test_format = lit.formats.GoogleTest('DummySubDir', 'Test') diff --git a/utils/lit/tests/Inputs/shtest-format/requires-any-missing.txt b/utils/lit/tests/Inputs/shtest-format/requires-any-missing.txt new file mode 100644 index 0000000000000..c977ee90c9e55 --- /dev/null +++ b/utils/lit/tests/Inputs/shtest-format/requires-any-missing.txt @@ -0,0 +1,2 @@ +RUN: true +REQUIRES-ANY: a-missing-feature, a-missing-feature-2 diff --git a/utils/lit/tests/Inputs/shtest-format/requires-any-present.txt b/utils/lit/tests/Inputs/shtest-format/requires-any-present.txt new file mode 100644 index 0000000000000..f3be518b25827 --- /dev/null +++ b/utils/lit/tests/Inputs/shtest-format/requires-any-present.txt @@ -0,0 +1,2 @@ +RUN: true +REQUIRES-ANY: a-missing-feature, a-present-feature diff --git a/utils/lit/tests/Inputs/shtest-output-printing/basic.txt b/utils/lit/tests/Inputs/shtest-output-printing/basic.txt new file mode 100644 index 0000000000000..4899c7e63db45 --- /dev/null +++ b/utils/lit/tests/Inputs/shtest-output-printing/basic.txt @@ -0,0 +1,3 @@ +# RUN: true +# RUN: echo hi +# RUN: wc missing-file &> %t.out diff --git a/utils/lit/tests/Inputs/shtest-output-printing/lit.cfg b/utils/lit/tests/Inputs/shtest-output-printing/lit.cfg new file mode 100644 index 0000000000000..4fe698d73368e --- /dev/null +++ b/utils/lit/tests/Inputs/shtest-output-printing/lit.cfg @@ -0,0 +1,4 @@ +import lit.formats +config.name = 'shtest-output-printing' +config.suffixes = ['.txt'] +config.test_format = lit.formats.ShTest(execute_external=False) diff --git a/utils/lit/tests/googletest-upstream-format.py b/utils/lit/tests/googletest-upstream-format.py new file mode 100644 index 0000000000000..1fc7c7c4a5ad2 --- /dev/null +++ b/utils/lit/tests/googletest-upstream-format.py @@ -0,0 +1,20 @@ +# Check the various features of the GoogleTest format. +# +# RUN: not %{lit} -j 1 -v %{inputs}/googletest-upstream-format > %t.out +# RUN: FileCheck < %t.out %s +# +# END. + +# CHECK: -- Testing: +# CHECK: PASS: googletest-upstream-format :: DummySubDir/OneTest/FirstTest.subTestA +# CHECK: FAIL: googletest-upstream-format :: DummySubDir/OneTest/FirstTest.subTestB +# CHECK-NEXT: *** TEST 'googletest-upstream-format :: DummySubDir/OneTest/FirstTest.subTestB' FAILED *** +# CHECK-NEXT: Running main() from gtest_main.cc +# CHECK-NEXT: I am subTest B, I FAIL +# CHECK-NEXT: And I have two lines of output +# CHECK: *** +# CHECK: PASS: googletest-upstream-format :: DummySubDir/OneTest/ParameterizedTest/0.subTest +# CHECK: PASS: googletest-upstream-format :: DummySubDir/OneTest/ParameterizedTest/1.subTest +# CHECK: Failing Tests (1) +# CHECK: Expected Passes : 3 +# CHECK: Unexpected Failures: 1 diff --git a/utils/lit/tests/shtest-format.py b/utils/lit/tests/shtest-format.py index 751f0d7080306..20884f8c4854d 100644 --- a/utils/lit/tests/shtest-format.py +++ b/utils/lit/tests/shtest-format.py @@ -39,14 +39,15 @@ # # CHECK: Command Output (stdout): # CHECK-NEXT: -- -# CHECK-NEXT: Command 0: "printf" -# CHECK-NEXT: Command 0 Result: 0 -# CHECK-NEXT: Command 0 Output: +# CHECK-NEXT: $ "printf" +# CHECK-NEXT: # command output: # CHECK-NEXT: line 1: failed test output on stdout # CHECK-NEXT: line 2: failed test output on stdout # CHECK: UNRESOLVED: shtest-format :: no-test-line.txt # CHECK: PASS: shtest-format :: pass.txt +# CHECK: UNSUPPORTED: shtest-format :: requires-any-missing.txt +# CHECK: PASS: shtest-format :: requires-any-present.txt # CHECK: UNSUPPORTED: shtest-format :: requires-missing.txt # CHECK: PASS: shtest-format :: requires-present.txt # CHECK: UNSUPPORTED: shtest-format :: unsupported_dir/some-test.txt @@ -69,9 +70,9 @@ # CHECK: shtest-format :: external_shell/fail_with_bad_encoding.txt # CHECK: shtest-format :: fail.txt -# CHECK: Expected Passes : 4 +# CHECK: Expected Passes : 5 # CHECK: Expected Failures : 3 -# CHECK: Unsupported Tests : 2 +# CHECK: Unsupported Tests : 3 # CHECK: Unresolved Tests : 1 # CHECK: Unexpected Passes : 1 # CHECK: Unexpected Failures: 3 diff --git a/utils/lit/tests/shtest-output-printing.py b/utils/lit/tests/shtest-output-printing.py new file mode 100644 index 0000000000000..24580b37f1f54 --- /dev/null +++ b/utils/lit/tests/shtest-output-printing.py @@ -0,0 +1,28 @@ +# Check the various features of the ShTest format. +# +# RUN: not %{lit} -j 1 -v %{inputs}/shtest-output-printing > %t.out +# RUN: FileCheck --input-file %t.out %s +# +# END. + +# CHECK: -- Testing: + +# CHECK: FAIL: shtest-output-printing :: basic.txt +# CHECK-NEXT: *** TEST 'shtest-output-printing :: basic.txt' FAILED *** +# CHECK-NEXT: Script: +# CHECK-NEXT: -- +# CHECK: -- +# CHECK-NEXT: Exit Code: 1 +# +# CHECK: Command Output +# CHECK-NEXT: -- +# CHECK-NEXT: $ "true" +# CHECK-NEXT: $ "echo" "hi" +# CHECK-NEXT: # command output: +# CHECK-NEXT: hi +# +# CHECK: $ "wc" "missing-file" +# CHECK-NEXT: # redirected output from '{{.*}}/basic.txt.tmp.out': +# CHECK-NEXT: missing-file{{.*}} No such file or directory +# CHECK: note: command had no output on stdout or stderr +# CHECK-NEXT: error: command failed with exit status: 1 diff --git a/utils/lit/tests/shtest-shell.py b/utils/lit/tests/shtest-shell.py index 32479e19a1026..18b80cd7d0877 100644 --- a/utils/lit/tests/shtest-shell.py +++ b/utils/lit/tests/shtest-shell.py @@ -1,7 +1,7 @@ # Check the internal shell handling component of the ShTest format. # # RUN: not %{lit} -j 1 -v %{inputs}/shtest-shell > %t.out -# RUN: FileCheck < %t.out %s +# RUN: FileCheck --input-file %t.out %s # # END. @@ -9,10 +9,10 @@ # CHECK: FAIL: shtest-shell :: error-0.txt # CHECK: *** TEST 'shtest-shell :: error-0.txt' FAILED *** -# CHECK: Command 0: "not-a-real-command" -# CHECK: Command 0 Result: 127 -# CHECK: Command 0 Stderr: +# CHECK: $ "not-a-real-command" +# CHECK: # command stderr: # CHECK: 'not-a-real-command': command not found +# CHECK: error: command failed with exit status: 127 # CHECK: *** # FIXME: The output here sucks. diff --git a/utils/llvm-build/llvmbuild/main.py b/utils/llvm-build/llvmbuild/main.py index f2472f698a671..fccfc7e6ece32 100644 --- a/utils/llvm-build/llvmbuild/main.py +++ b/utils/llvm-build/llvmbuild/main.py @@ -413,7 +413,7 @@ subdirectories = %s if library_name is None: library_name_as_cstr = 'nullptr' else: - library_name_as_cstr = '"lib%s.a"' % library_name + library_name_as_cstr = '"%s"' % library_name if is_installed: is_installed_as_cstr = 'true' else: diff --git a/utils/llvm-lit/Makefile b/utils/llvm-lit/Makefile deleted file mode 100644 index ce1cac9c32e90..0000000000000 --- a/utils/llvm-lit/Makefile +++ /dev/null @@ -1,27 +0,0 @@ -##===- utils/llvm-lit/Makefile -----------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../.. - -include $(LEVEL)/Makefile.common - -# llvm-lit needs suffix.py for multiprocess to find a main module. -ifeq ($(HOST_OS),MingW) - Suffix := .py -endif - -all:: $(ToolDir)/llvm-lit$(Suffix) - -$(ToolDir)/llvm-lit$(Suffix): llvm-lit.in Makefile $(ToolDir)/.dir - $(Echo) "Creating 'llvm-lit' script..." - $(Verb)$(ECHOPATH) s=@LLVM_SOURCE_DIR@=$(LLVM_SRC_ROOT)=g > lit.tmp - $(Verb)$(ECHOPATH) s=@LLVM_BINARY_DIR@=$(LLVM_OBJ_ROOT)=g >> lit.tmp - $(Verb)sed -f lit.tmp $< > $@ - $(Verb)chmod +x $@ - $(Verb)rm -f lit.tmp diff --git a/utils/llvm.natvis b/utils/llvm.natvis deleted file mode 100644 index 6c410a40ecbdc..0000000000000 --- a/utils/llvm.natvis +++ /dev/null @@ -1,169 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?>
-<!--
-Visual Studio 2012 Native Debugging Visualizers for LLVM
-
-Put this file into "%USERPROFILE%\Documents\Visual Studio 2012\Visualizers"
-or create a symbolic link so it updates automatically.
--->
-<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
-
- <Type Name="llvm::SmallVectorImpl<*>">
- <DisplayString Condition="(($T1*)EndX - ($T1*)BeginX) == 0">empty</DisplayString>
- <DisplayString Condition="(($T1*)EndX - ($T1*)BeginX) != 0">{{ size={($T1*)EndX - ($T1*)BeginX} }}</DisplayString>
- <Expand>
- <Item Name="[size]">($T1*)EndX - ($T1*)BeginX</Item>
- <Item Name="[capacity]">($T1*)CapacityX - ($T1*)BeginX</Item>
- <ArrayItems>
- <Size>($T1*)EndX - ($T1*)BeginX</Size>
- <ValuePointer>($T1*)BeginX</ValuePointer>
- </ArrayItems>
- </Expand>
- </Type>
-
- <Type Name="llvm::SmallString<*>">
- <DisplayString>{BeginX,s}</DisplayString>
- <StringView>BeginX,s</StringView>
- <Expand>
- <Item Name="[size]">(char*)EndX - (char*)BeginX</Item>
- <Item Name="[capacity]">(char*)CapacityX - (char*)BeginX</Item>
- <ArrayItems>
- <Size>(char*)EndX - (char*)BeginX</Size>
- <ValuePointer>(char*)BeginX</ValuePointer>
- </ArrayItems>
- </Expand>
- </Type>
-
- <Type Name="llvm::StringRef">
- <DisplayString>{Data,[Length]s}</DisplayString>
- <StringView>Data,[Length]s</StringView>
- <Expand>
- <Item Name="[size]">Length</Item>
- <ArrayItems>
- <Size>Length</Size>
- <ValuePointer>Data</ValuePointer>
- </ArrayItems>
- </Expand>
- </Type>
-
- <Type Name="llvm::PointerIntPair<*,*,*,*>">
- <DisplayString>{(void*)(Value & PointerBitMask)} [{($T3)((Value >> IntShift) & IntMask)}]</DisplayString>
- <Expand>
- <Item Name="[ptr]">($T1*)(Value & PointerBitMask)</Item>
- <Item Name="[int]">($T3)((Value >> IntShift) & IntMask)</Item>
- </Expand>
- </Type>
-
- <Type Name="llvm::PointerUnion<*,*>">
- <DisplayString Condition="((Val.Value >> Val.IntShift) & Val.IntMask) == 0">{"$T1", s8b} {(void*)(Val.Value & Val.PointerBitMask)}</DisplayString>
- <DisplayString Condition="((Val.Value >> Val.IntShift) & Val.IntMask) != 0">{"$T2", s8b} {(void*)(Val.Value & Val.PointerBitMask)}</DisplayString>
- <Expand>
- <ExpandedItem Condition="((Val.Value >> Val.IntShift) & Val.IntMask) == 0">($T1)(Val.Value & Val.PointerBitMask)</ExpandedItem>
- <ExpandedItem Condition="((Val.Value >> Val.IntShift) & Val.IntMask) != 0">($T2)(Val.Value & Val.PointerBitMask)</ExpandedItem>
- </Expand>
- </Type>
-
- <Type Name="llvm::PointerUnion3<*,*,*>">
- <DisplayString Condition="(Val.Val.Value & 2) != 2 && (Val.Val.Value & 1) != 1">{"$T1", s8b} {(void*)((Val.Val.Value >> 2) << 2)}</DisplayString>
- <DisplayString Condition="(Val.Val.Value & 2) == 2">{"$T2", s8b} {(void*)((Val.Val.Value >> 2) << 2)}</DisplayString>
- <DisplayString Condition="(Val.Val.Value & 1) == 1">{"$T3", s8b} {(void*)((Val.Val.Value >> 2) << 2)}</DisplayString>
- <Expand>
- <ExpandedItem Condition="(Val.Val.Value & 2) != 2 && (Val.Val.Value & 1) != 1">($T1)((Val.Val.Value >> 2) << 2)</ExpandedItem>
- <ExpandedItem Condition="(Val.Val.Value & 2) == 2">($T2)((Val.Val.Value >> 2) << 2)</ExpandedItem>
- <ExpandedItem Condition="(Val.Val.Value & 1) == 1">($T3)((Val.Val.Value >> 2) << 2)</ExpandedItem>
- </Expand>
- </Type>
-
- <Type Name="llvm::PointerUnion4<*,*,*,*>">
- <DisplayString Condition="(Val.Val.Value & 3) != 3 && (Val.Val.Value & 2) != 2 && (Val.Val.Value & 1) != 1">{"$T1", s8b} {(void*)((Val.Val.Value >> 2) << 2)}</DisplayString>
- <DisplayString Condition="(Val.Val.Value & 3) != 3 && (Val.Val.Value & 2) == 2">{"$T2", s8b} {(void*)((Val.Val.Value >> 2) << 2)}</DisplayString>
- <DisplayString Condition="(Val.Val.Value & 3) != 3 && (Val.Val.Value & 1) == 1">{"$T3", s8b} {(void*)((Val.Val.Value >> 2) << 2)}</DisplayString>
- <DisplayString Condition="(Val.Val.Value & 3) == 3">{"$T4", s8b} {(void*)((Val.Val.Value >> 2) << 2)}</DisplayString>
- <Expand>
- <ExpandedItem Condition="(Val.Val.Value & 3) != 3 && (Val.Val.Value & 2) != 2 && (Val.Val.Value & 1) != 1">($T1)((Val.Val.Value >> 2) << 2)</ExpandedItem>
- <ExpandedItem Condition="(Val.Val.Value & 3) != 3 && (Val.Val.Value & 2) == 2">($T2)((Val.Val.Value >> 2) << 2)</ExpandedItem>
- <ExpandedItem Condition="(Val.Val.Value & 3) != 3 && (Val.Val.Value & 1) == 1">($T3)((Val.Val.Value >> 2) << 2)</ExpandedItem>
- <ExpandedItem Condition="(Val.Val.Value & 3) == 3">($T4)((Val.Val.Value >> 2) << 2)</ExpandedItem>
- </Expand>
- </Type>
-
- <Type Name="llvm::iplist<*,*>">
- <DisplayString Condition="Head == 0">{{ empty }}</DisplayString>
- <DisplayString Condition="Head != 0">{{ head={Head} }}</DisplayString>
- <Expand>
- <LinkedListItems>
- <HeadPointer>Head</HeadPointer>
- <NextPointer>Next</NextPointer>
- <ValueNode>this</ValueNode>
- </LinkedListItems>
- </Expand>
- </Type>
-
- <Type Name="llvm::IntrusiveRefCntPtr<*>">
- <DisplayString Condition="Obj == 0">empty</DisplayString>
- <DisplayString Condition="(Obj != 0) && (Obj->ref_cnt == 1)">RefPtr [1 ref] {*Obj}</DisplayString>
- <DisplayString Condition="(Obj != 0) && (Obj->ref_cnt != 1)">RefPtr [{Obj->ref_cnt} refs] {*Obj}</DisplayString>
- <Expand>
- <Item Condition="Obj != 0" Name="[refs]">Obj->ref_cnt</Item>
- <ExpandedItem Condition="Obj != 0">Obj</ExpandedItem>
- </Expand>
- </Type>
-
- <Type Name="llvm::SmallPtrSet<*,*>">
- <DisplayString Condition="CurArray == SmallArray">{{ [Small Mode] size={NumElements}, capacity={CurArraySize} }}</DisplayString>
- <DisplayString Condition="CurArray != SmallArray">{{ [Big Mode] size={NumElements}, capacity={CurArraySize} }}</DisplayString>
- <Expand>
- <Item Name="[size]">NumElements</Item>
- <Item Name="[capacity]">CurArraySize</Item>
- <ArrayItems>
- <Size>CurArraySize</Size>
- <ValuePointer>($T1*)CurArray</ValuePointer>
- </ArrayItems>
- </Expand>
- </Type>
-
- <Type Name="llvm::DenseMap<*,*,*>">
- <DisplayString Condition="NumEntries == 0">empty</DisplayString>
- <DisplayString Condition="NumEntries != 0">{{ size={NumEntries}, buckets={NumBuckets} }}</DisplayString>
- <Expand>
- <Item Name="[size]">NumEntries</Item>
- <Item Name="[buckets]">NumBuckets</Item>
- <ArrayItems>
- <Size>NumBuckets</Size>
- <ValuePointer>Buckets</ValuePointer>
- </ArrayItems>
- </Expand>
- </Type>
-
- <Type Name="llvm::StringMap<*,*>">
- <DisplayString>{{ size={NumItems}, buckets={NumBuckets} }}</DisplayString>
- <Expand>
- <Item Name="[size]">NumItems</Item>
- <Item Name="[buckets]">NumBuckets</Item>
- <ArrayItems>
- <Size>NumBuckets</Size>
- <ValuePointer>(MapEntryTy**)TheTable</ValuePointer>
- </ArrayItems>
- </Expand>
- </Type>
-
- <Type Name="llvm::StringMapEntry<*>">
- <DisplayString Condition="StrLen == 0">empty</DisplayString>
- <DisplayString Condition="StrLen != 0">({this+1,s}, {second})</DisplayString>
- <Expand>
- <Item Name="[key]">this+1,s</Item>
- <Item Name="[value]" Condition="StrLen != 0">second</Item>
- </Expand>
- </Type>
-
- <Type Name="llvm::Triple">
- <DisplayString>{Data}</DisplayString>
- </Type>
-
- <Type Name="llvm::Optional<*>">
- <DisplayString Condition="!hasVal">empty</DisplayString>
- <DisplayString Condition="hasVal">{*(($T1 *)(unsigned char *)storage.buffer)}</DisplayString>
- <Expand>
- <Item Name="[underlying]" Condition="hasVal">*(($T1 *)(unsigned char *)storage.buffer)</Item>
- </Expand>
- </Type>
-</AutoVisualizer>
diff --git a/utils/not/Makefile b/utils/not/Makefile deleted file mode 100644 index 26b7450d48b07..0000000000000 --- a/utils/not/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -##===- utils/not/Makefile ----------------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../.. -TOOLNAME = not -USEDLIBS = LLVMSupport.a - -# This tool has no plugins, optimize startup time. -TOOL_NO_EXPORTS = 1 - -# FIXME: Don't install this utility -#NO_INSTALL = 1 - -include $(LEVEL)/Makefile.common - diff --git a/utils/prepare-code-coverage-artifact.py b/utils/prepare-code-coverage-artifact.py new file mode 100644 index 0000000000000..e233c2c6efec0 --- /dev/null +++ b/utils/prepare-code-coverage-artifact.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python + +'''Prepare a code coverage artifact. + +- Collate raw profiles into one indexed profile. +- Delete the raw profiles. +- Copy the coverage mappings in the binaries directory. +''' + +import argparse +import glob +import os +import subprocess +import sys + +def merge_raw_profiles(host_llvm_profdata, profile_data_dir): + print ':: Merging raw profiles...', + sys.stdout.flush() + raw_profiles = glob.glob(os.path.join(profile_data_dir, '*.profraw')) + manifest_path = os.path.join(profile_data_dir, 'profiles.manifest') + profdata_path = os.path.join(profile_data_dir, 'Coverage.profdata') + with open(manifest_path, 'w') as manifest: + manifest.write('\n'.join(raw_profiles)) + subprocess.check_call([host_llvm_profdata, 'merge', '-sparse', '-f', + manifest_path, '-o', profdata_path]) + for raw_profile in raw_profiles: + os.remove(raw_profile) + print 'Done!' + +def extract_covmappings(host_llvm_cov, profile_data_dir, llvm_bin_dir): + print ':: Extracting covmappings...', + sys.stdout.flush() + for prog in os.listdir(llvm_bin_dir): + if prog == 'llvm-lit': + continue + covmapping_path = os.path.join(profile_data_dir, + os.path.basename(prog) + '.covmapping') + subprocess.check_call([host_llvm_cov, 'convert-for-testing', + os.path.join(llvm_bin_dir, prog), '-o', + covmapping_path]) + print 'Done!' + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('host_llvm_profdata', help='Path to llvm-profdata') + parser.add_argument('host_llvm_cov', help='Path to llvm-cov') + parser.add_argument('profile_data_dir', + help='Path to the directory containing the raw profiles') + parser.add_argument('llvm_bin_dir', + help='Path to the directory containing llvm binaries') + args = parser.parse_args() + + merge_raw_profiles(args.host_llvm_profdata, args.profile_data_dir) + extract_covmappings(args.host_llvm_cov, args.profile_data_dir, + args.llvm_bin_dir) diff --git a/utils/release/build_llvm_package.bat b/utils/release/build_llvm_package.bat index 830f25e5cf389..5542ca613f310 100755 --- a/utils/release/build_llvm_package.bat +++ b/utils/release/build_llvm_package.bat @@ -19,8 +19,8 @@ set PATH=%PATH%;c:\gnuwin32\bin set revision=%1
set branch=trunk
-set package_version=3.8.0-r%revision%
-set clang_format_vs_version=3.8.0.%revision%
+set package_version=3.9.0-r%revision%
+set clang_format_vs_version=3.9.0.%revision%
set build_dir=llvm_package_%revision%
echo Branch: %branch%
@@ -40,9 +40,11 @@ svn.exe export -r %revision% http://llvm.org/svn/llvm-project/cfe/%branch% llvm/ svn.exe export -r %revision% http://llvm.org/svn/llvm-project/clang-tools-extra/%branch% llvm/tools/clang/tools/extra || exit /b
svn.exe export -r %revision% http://llvm.org/svn/llvm-project/lld/%branch% llvm/tools/lld || exit /b
svn.exe export -r %revision% http://llvm.org/svn/llvm-project/compiler-rt/%branch% llvm/projects/compiler-rt || exit /b
+svn.exe export -r %revision% http://llvm.org/svn/llvm-project/openmp/%branch% llvm/projects/openmp || exit /b
-set cmake_flags=-DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON -DLLVM_INSTALL_TOOLCHAIN_ONLY=ON -DLLVM_USE_CRT_RELEASE=MT -DCLANG_FORMAT_VS_VERSION=%clang_format_vs_version% -DPACKAGE_VERSION=%package_version%
+REM Setting CMAKE_CL_SHOWINCLUDES_PREFIX to work around PR27226.
+set cmake_flags=-DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON -DLLVM_INSTALL_TOOLCHAIN_ONLY=ON -DLLVM_USE_CRT_RELEASE=MT -DCLANG_FORMAT_VS_VERSION=%clang_format_vs_version% -DPACKAGE_VERSION=%package_version% -DCMAKE_CL_SHOWINCLUDES_PREFIX="Note: including file: "
REM TODO: Run all tests, including lld and compiler-rt.
diff --git a/utils/release/export.sh b/utils/release/export.sh index 2fd4206b74062..d978055f01837 100755 --- a/utils/release/export.sh +++ b/utils/release/export.sh @@ -20,7 +20,7 @@ base_url="https://llvm.org/svn/llvm-project" release="" rc="" -function usage() { +usage() { echo "Export the SVN sources and build tarballs from them" echo "usage: `basename $0`" echo " " @@ -29,7 +29,7 @@ function usage() { echo " -final The final tag" } -function export_sources() { +export_sources() { release_no_dot=`echo $release | sed -e 's,\.,,g'` tag_dir="tags/RELEASE_$release_no_dot/$rc" diff --git a/utils/release/merge.sh b/utils/release/merge.sh index 93e08c7ce1357..b48932248347d 100755 --- a/utils/release/merge.sh +++ b/utils/release/merge.sh @@ -17,12 +17,14 @@ set -e rev="" proj="" revert="no" +srcdir="" -function usage() { +usage() { echo "usage: `basename $0` [OPTIONS]" echo " -proj PROJECT The project to merge the result into" echo " -rev NUM The revision to merge into the project" echo " -revert Revert rather than merge the commit" + echo " -srcdir The root of the project checkout" } while [ $# -gt 0 ]; do @@ -35,6 +37,10 @@ while [ $# -gt 0 ]; do shift proj=$1 ;; + --srcdir | -srcdir | -s) + shift + srcdir=$1 + ;; -h | -help | --help ) usage ;; @@ -51,6 +57,10 @@ while [ $# -gt 0 ]; do shift done +if [ -z "$srcdir" ]; then + srcdir="$proj.src" +fi + if [ "x$rev" = "x" -o "x$proj" = "x" ]; then echo "error: need to specify project and revision" echo @@ -72,7 +82,7 @@ else fi svn log -c $rev http://llvm.org/svn/llvm-project/$proj/trunk >> $tempfile 2>&1 -cd $proj.src +cd "$srcdir" echo "# Updating tree" svn up diff --git a/utils/release/tag.sh b/utils/release/tag.sh index caefc7f7b71bd..c3e839d932385 100755 --- a/utils/release/tag.sh +++ b/utils/release/tag.sh @@ -23,7 +23,7 @@ revision="HEAD" base_url="https://llvm.org/svn/llvm-project" -function usage() { +usage() { echo "usage: `basename $0` -release <num> [-rebranch] [-revision <num>] [-dry-run]" echo "usage: `basename $0` -release <num> -rc <num> [-dry-run]" echo " " @@ -35,7 +35,7 @@ function usage() { echo " -dry-run Make no changes to the repository, just print the commands" } -function tag_version() { +tag_version() { set -x for proj in $projects; do if svn ls $base_url/$proj/branches/release_$branch_release > /dev/null 2>&1 ; then @@ -53,7 +53,7 @@ function tag_version() { set +x } -function tag_release_candidate() { +tag_release_candidate() { set -x for proj in $projects ; do if ! svn ls $base_url/$proj/tags/RELEASE_$tag_release > /dev/null 2>&1 ; then diff --git a/utils/release/test-release.sh b/utils/release/test-release.sh index 69de8371d6318..37af976ec0c50 100755 --- a/utils/release/test-release.sh +++ b/utils/release/test-release.sh @@ -12,7 +12,8 @@ # #===------------------------------------------------------------------------===# -if [ `uname -s` = "FreeBSD" ]; then +System=`uname -s` +if [ "$System" = "FreeBSD" ]; then MAKE=gmake else MAKE=make @@ -35,6 +36,7 @@ do_libs="yes" do_libunwind="yes" do_test_suite="yes" do_openmp="yes" +do_lldb="no" BuildDir="`pwd`" use_autoconf="no" ExtraConfigureFlags="" @@ -63,6 +65,8 @@ function usage() { echo " -no-libunwind Disable check-out & build libunwind" echo " -no-test-suite Disable check-out & build test-suite" echo " -no-openmp Disable check-out & build libomp" + echo " -lldb Enable check-out & build lldb" + echo " -no-lldb Disable check-out & build lldb (default)" } while [ $# -gt 0 ]; do @@ -141,6 +145,12 @@ while [ $# -gt 0 ]; do -no-openmp ) do_openmp="no" ;; + -lldb ) + do_lldb="yes" + ;; + -no-lldb ) + do_lldb="no" + ;; -help | --help | -h | --h | -\? ) usage exit 0 @@ -213,6 +223,9 @@ esac if [ $do_openmp = "yes" ]; then projects="$projects openmp" fi +if [ $do_lldb = "yes" ]; then + projects="$projects lldb" +fi # Go to the build directory (may be different from CWD) BuildDir=$BuildDir/$RC @@ -249,7 +262,7 @@ function check_program_exists() { fi } -if [ `uname -s` != "Darwin" ]; then +if [ "$System" != "Darwin" ]; then check_program_exists 'chrpath' check_program_exists 'file' check_program_exists 'objdump' @@ -279,6 +292,9 @@ function export_sources() { cfe) projsrc=llvm.src/tools/clang ;; + lldb) + projsrc=llvm.src/tools/$proj + ;; clang-tools-extra) projsrc=llvm.src/tools/clang/tools/extra ;; @@ -359,13 +375,13 @@ function configure_llvmCore() { echo "#" env CC="$c_compiler" CXX="$cxx_compiler" \ cmake -G "Unix Makefiles" \ -DCMAKE_BUILD_TYPE=$BuildType -DLLVM_ENABLE_ASSERTIONS=$Assertions \ - -DLLVM_ENABLE_TIMESTAMPS=OFF -DLLVM_CONFIGTIME="(timestamp not enabled)" \ + -DLLVM_CONFIGTIME="(timestamp not enabled)" \ $ExtraConfigureFlags $BuildDir/llvm.src \ 2>&1 | tee $LogDir/llvm.configure-Phase$Phase-$Flavor.log env CC="$c_compiler" CXX="$cxx_compiler" \ cmake -G "Unix Makefiles" \ -DCMAKE_BUILD_TYPE=$BuildType -DLLVM_ENABLE_ASSERTIONS=$Assertions \ - -DLLVM_ENABLE_TIMESTAMPS=OFF -DLLVM_CONFIGTIME="(timestamp not enabled)" \ + -DLLVM_CONFIGTIME="(timestamp not enabled)" \ $ExtraConfigureFlags $BuildDir/llvm.src \ 2>&1 | tee $LogDir/llvm.configure-Phase$Phase-$Flavor.log fi @@ -418,7 +434,7 @@ function test_llvmCore() { # Clean RPATH. Libtool adds the build directory to the search path, which is # not necessary --- and even harmful --- for the binary packages we release. function clean_RPATH() { - if [ `uname -s` = "Darwin" ]; then + if [ "$System" = "Darwin" ]; then return fi local InstallPath="$1" diff --git a/utils/unittest/Makefile b/utils/unittest/Makefile deleted file mode 100644 index 6a09341832bc3..0000000000000 --- a/utils/unittest/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -##===- utils/unittest/Makefile -----------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../.. -PARALLEL_DIRS = googletest UnitTestMain - -include $(LEVEL)/Makefile.common diff --git a/utils/unittest/UnitTestMain/Makefile b/utils/unittest/UnitTestMain/Makefile deleted file mode 100644 index 7bcb724950492..0000000000000 --- a/utils/unittest/UnitTestMain/Makefile +++ /dev/null @@ -1,32 +0,0 @@ -##===- utils/unittest/UnitTestMain/Makefile ----------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../../.. - -include $(LEVEL)/Makefile.config - -LIBRARYNAME = gtest_main -BUILD_ARCHIVE = 1 -REQUIRES_RTTI = 1 - -CPP.Flags += -I$(LLVM_SRC_ROOT)/utils/unittest/googletest/include -CPP.Flags += $(NO_MISSING_FIELD_INITIALIZERS) $(NO_VARIADIC_MACROS) -CPP.Flags += -DGTEST_HAS_RTTI=0 -# libstdc++'s TR1 <tuple> header depends on RTTI and uses C++'0x features not -# supported by Clang, so force googletest to use its own tuple implementation. -CPP.Flags += -DGTEST_USE_OWN_TR1_TUPLE - -# Disable pthreads if LLVM was configured without them. -ifneq ($(HAVE_PTHREAD), 1) - CPP.Flags += -DGTEST_HAS_PTHREAD=0 -endif - -NO_INSTALL = 1 - -include $(LEVEL)/Makefile.common diff --git a/utils/unittest/UnitTestMain/TestMain.cpp b/utils/unittest/UnitTestMain/TestMain.cpp index fb2b0f16ee3f2..36cec2d474249 100644 --- a/utils/unittest/UnitTestMain/TestMain.cpp +++ b/utils/unittest/UnitTestMain/TestMain.cpp @@ -22,7 +22,8 @@ const char *TestMainArgv0; int main(int argc, char **argv) { - llvm::sys::PrintStackTraceOnErrorSignal(true /* Disable crash reporting */); + llvm::sys::PrintStackTraceOnErrorSignal(argv[0], + true /* Disable crash reporting */); testing::InitGoogleTest(&argc, argv); llvm::cl::ParseCommandLineOptions(argc, argv); diff --git a/utils/unittest/googletest/Makefile b/utils/unittest/googletest/Makefile deleted file mode 100644 index 3d85e7da0ba60..0000000000000 --- a/utils/unittest/googletest/Makefile +++ /dev/null @@ -1,42 +0,0 @@ -##===- utils/unittest/googletest/Makefile ------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL := ../../.. - -include $(LEVEL)/Makefile.config - -LIBRARYNAME = gtest -BUILD_ARCHIVE = 1 -REQUIRES_RTTI = 1 - -# Note that these flags are duplicated when building individual tests in -# unittests/Makefile.unittest and ../UnitTestMain/Makefile; ensure that any -# changes are made to both. -CPP.Flags += -I$(LLVM_SRC_ROOT)/utils/unittest/googletest/include -CPP.Flags += -I$(LLVM_SRC_ROOT)/utils/unittest/googletest -CPP.Flags += $(NO_MISSING_FIELD_INITIALIZERS) $(NO_VARIADIC_MACROS) -CPP.Flags += -DGTEST_HAS_RTTI=0 -# libstdc++'s TR1 <tuple> header depends on RTTI and uses C++'0x features not -# supported by Clang, so force googletest to use its own tuple implementation. -CPP.Flags += -DGTEST_USE_OWN_TR1_TUPLE - -# Disable pthreads if LLVM was configured without them. -ifneq ($(HAVE_PTHREAD), 1) - CPP.Flags += -DGTEST_HAS_PTHREAD=0 -endif - -ifeq ($(HOST_OS),MingW) - CPP.Flags += -DGTEST_OS_WINDOWS=1 -endif - -NO_INSTALL = 1 - -SOURCES = src/gtest-all.cc - -include $(LEVEL)/Makefile.common diff --git a/utils/unittest/googletest/README.LLVM b/utils/unittest/googletest/README.LLVM index 5f7fffe26a43f..afaae9963f391 100644 --- a/utils/unittest/googletest/README.LLVM +++ b/utils/unittest/googletest/README.LLVM @@ -17,6 +17,7 @@ $ mv COPYING LICENSE.TXT Modified as follows: * Added support for FreeBSD. +* Added support for Minix (PR6797). * To GTestStreamToHelper in include/gtest/internal/gtest-internal.h, added the ability to stream with raw_os_ostream. * To refresh Haiku support in include/gtest/internal/gtest-port.h, diff --git a/utils/unittest/googletest/include/gtest/internal/gtest-port.h b/utils/unittest/googletest/include/gtest/internal/gtest-port.h index 6b942e9f9d9e7..cac04a7bb72be 100644 --- a/utils/unittest/googletest/include/gtest/internal/gtest-port.h +++ b/utils/unittest/googletest/include/gtest/internal/gtest-port.h @@ -92,6 +92,7 @@ // GTEST_OS_LINUX - Linux // GTEST_OS_LINUX_ANDROID - Google Android // GTEST_OS_MAC - Mac OS X +// GTEST_OS_MINIX - Minix // GTEST_OS_NACL - Google Native Client (NaCl) // GTEST_OS_SOLARIS - Sun Solaris // GTEST_OS_SYMBIAN - Symbian @@ -248,6 +249,8 @@ # define GTEST_OS_NACL 1 #elif defined(__HAIKU__) # define GTEST_OS_HAIKU 1 +#elif defined(_MINIX) +# define GTEST_OS_MINIX 1 #endif // __CYGWIN__ // Brings in definitions for functions used in the testing::internal::posix @@ -359,7 +362,7 @@ // no support for it at least as recent as Froyo (2.2). // Minix currently doesn't support it either. # define GTEST_HAS_STD_WSTRING \ - (!(GTEST_OS_LINUX_ANDROID || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || GTEST_OS_HAIKU || defined(_MINIX))) + (!(GTEST_OS_LINUX_ANDROID || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || GTEST_OS_HAIKU || GTEST_OS_MINIX)) #endif // GTEST_HAS_STD_WSTRING diff --git a/utils/update_llc_test_checks.py b/utils/update_llc_test_checks.py index cfdf830907f50..d2df5b6511e15 100755 --- a/utils/update_llc_test_checks.py +++ b/utils/update_llc_test_checks.py @@ -9,50 +9,132 @@ a single test function. import argparse import itertools +import os # Used to advertise this file's name ("autogenerated_note"). import string import subprocess import sys import tempfile import re - +# Invoke the tool that is being tested. def llc(args, cmd_args, ir): with open(ir) as ir_file: stdout = subprocess.check_output(args.llc_binary + ' ' + cmd_args, shell=True, stdin=ir_file) + # Fix line endings to unix CR style. + stdout = stdout.replace('\r\n', '\n') return stdout -ASM_SCRUB_WHITESPACE_RE = re.compile(r'(?!^(| \w))[ \t]+', flags=re.M) -ASM_SCRUB_TRAILING_WHITESPACE_RE = re.compile(r'[ \t]+$', flags=re.M) -ASM_SCRUB_SHUFFLES_RE = ( +# RegEx: this is where the magic happens. + +SCRUB_WHITESPACE_RE = re.compile(r'(?!^(| \w))[ \t]+', flags=re.M) +SCRUB_TRAILING_WHITESPACE_RE = re.compile(r'[ \t]+$', flags=re.M) +SCRUB_X86_SHUFFLES_RE = ( re.compile( - r'^(\s*\w+) [^#\n]+#+ ((?:[xyz]mm\d+|mem) = .*)$', + r'^(\s*\w+) [^#\n]+#+ ((?:[xyz]mm\d+|mem)( \{%k\d+\}( \{z\})?)? = .*)$', flags=re.M)) -ASM_SCRUB_SP_RE = re.compile(r'\d+\(%(esp|rsp)\)') -ASM_SCRUB_RIP_RE = re.compile(r'[.\w]+\(%rip\)') -ASM_SCRUB_KILL_COMMENT_RE = re.compile(r'^ *#+ +kill:.*\n') +SCRUB_X86_SP_RE = re.compile(r'\d+\(%(esp|rsp)\)') +SCRUB_X86_RIP_RE = re.compile(r'[.\w]+\(%rip\)') +SCRUB_X86_LCP_RE = re.compile(r'\.LCPI[0-9]+_[0-9]+') +SCRUB_KILL_COMMENT_RE = re.compile(r'^ *#+ +kill:.*\n') + +RUN_LINE_RE = re.compile('^\s*;\s*RUN:\s*(.*)$') +IR_FUNCTION_RE = re.compile('^\s*define\s+(?:internal\s+)?[^@]*@(\w+)\s*\(') +ASM_FUNCTION_RE = re.compile( + r'^_?(?P<func>[^:]+):[ \t]*#+[ \t]*@(?P=func)\n[^:]*?' + r'(?P<body>^##?[ \t]+[^:]+:.*?)\s*' + r'^\s*(?:[^:\n]+?:\s*\n\s*\.size|\.cfi_endproc|\.globl|\.comm|\.(?:sub)?section)', + flags=(re.M | re.S)) +CHECK_PREFIX_RE = re.compile('--check-prefix=(\S+)') +CHECK_RE = re.compile(r'^\s*;\s*([^:]+?)(?:-NEXT|-NOT|-DAG|-LABEL)?:') def scrub_asm(asm): # Scrub runs of whitespace out of the assembly, but leave the leading # whitespace in place. - asm = ASM_SCRUB_WHITESPACE_RE.sub(r' ', asm) + asm = SCRUB_WHITESPACE_RE.sub(r' ', asm) # Expand the tabs used for indentation. asm = string.expandtabs(asm, 2) # Detect shuffle asm comments and hide the operands in favor of the comments. - asm = ASM_SCRUB_SHUFFLES_RE.sub(r'\1 {{.*#+}} \2', asm) + asm = SCRUB_X86_SHUFFLES_RE.sub(r'\1 {{.*#+}} \2', asm) # Generically match the stack offset of a memory operand. - asm = ASM_SCRUB_SP_RE.sub(r'{{[0-9]+}}(%\1)', asm) + asm = SCRUB_X86_SP_RE.sub(r'{{[0-9]+}}(%\1)', asm) # Generically match a RIP-relative memory operand. - asm = ASM_SCRUB_RIP_RE.sub(r'{{.*}}(%rip)', asm) + asm = SCRUB_X86_RIP_RE.sub(r'{{.*}}(%rip)', asm) + # Generically match a LCP symbol. + asm = SCRUB_X86_LCP_RE.sub(r'{{\.LCPI.*}}', asm) # Strip kill operands inserted into the asm. - asm = ASM_SCRUB_KILL_COMMENT_RE.sub('', asm) + asm = SCRUB_KILL_COMMENT_RE.sub('', asm) # Strip trailing whitespace. - asm = ASM_SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm) + asm = SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm) return asm +# Build up a dictionary of all the function bodies. +def build_function_body_dictionary(raw_tool_output, prefixes, func_dict, verbose): + for m in ASM_FUNCTION_RE.finditer(raw_tool_output): + if not m: + continue + func = m.group('func') + scrubbed_body = scrub_asm(m.group('body')) + if func.startswith('stress'): + # We only use the last line of the function body for stress tests. + scrubbed_body = '\n'.join(scrubbed_body.splitlines()[-1:]) + if verbose: + print >>sys.stderr, 'Processing function: ' + func + for l in scrubbed_body.splitlines(): + print >>sys.stderr, ' ' + l + for prefix in prefixes: + if func in func_dict[prefix] and func_dict[prefix][func] != scrubbed_body: + if prefix == prefixes[-1]: + print >>sys.stderr, ('WARNING: Found conflicting asm under the ' + 'same prefix: %r!' % (prefix,)) + else: + func_dict[prefix][func] = None + continue + + func_dict[prefix][func] = scrubbed_body + + +def add_checks(output_lines, prefix_list, func_dict, func_name): + printed_prefixes = [] + for checkprefixes, _ in prefix_list: + for checkprefix in checkprefixes: + if checkprefix in printed_prefixes: + break + if not func_dict[checkprefix][func_name]: + continue + # Add some space between different check prefixes. + if len(printed_prefixes) != 0: + output_lines.append(';') + printed_prefixes.append(checkprefix) + output_lines.append('; %s-LABEL: %s:' % (checkprefix, func_name)) + func_body = func_dict[checkprefix][func_name].splitlines() + output_lines.append('; %s: %s' % (checkprefix, func_body[0])) + for func_line in func_body[1:]: + output_lines.append('; %s-NEXT: %s' % (checkprefix, func_line)) + # Add space between different check prefixes and the first line of code. + # output_lines.append(';') + break + return output_lines + + +def should_add_line_to_output(input_line, prefix_set): + # Skip any blank comment lines in the IR. + if input_line.strip() == ';': + return False + # Skip any blank lines in the IR. + #if input_line.strip() == '': + # return False + # And skip any CHECK lines. We're building our own. + m = CHECK_RE.match(input_line) + if m and m.group(1) in prefix_set: + return False + + return True + + def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('-v', '--verbose', action='store_true', @@ -64,32 +146,23 @@ def main(): parser.add_argument('tests', nargs='+') args = parser.parse_args() - run_line_re = re.compile('^\s*;\s*RUN:\s*(.*)$') - ir_function_re = re.compile('^\s*define\s+(?:internal\s+)?[^@]*@(\w+)\s*\(') - asm_function_re = re.compile( - r'^_?(?P<f>[^:]+):[ \t]*#+[ \t]*@(?P=f)\n[^:]*?' - r'(?P<body>^##?[ \t]+[^:]+:.*?)\s*' - r'^\s*(?:[^:\n]+?:\s*\n\s*\.size|\.cfi_endproc|\.globl|\.comm|\.(?:sub)?section)', - flags=(re.M | re.S)) - check_prefix_re = re.compile('--check-prefix=(\S+)') - check_re = re.compile(r'^\s*;\s*([^:]+?)(?:-NEXT|-NOT|-DAG|-LABEL)?:') autogenerated_note = ('; NOTE: Assertions have been autogenerated by ' - 'utils/update_llc_test_checks.py') + 'utils/' + os.path.basename(__file__)) for test in args.tests: if args.verbose: print >>sys.stderr, 'Scanning for RUN lines in test file: %s' % (test,) with open(test) as f: - test_lines = [l.rstrip() for l in f] + input_lines = [l.rstrip() for l in f] run_lines = [m.group(1) - for m in [run_line_re.match(l) for l in test_lines] if m] + for m in [RUN_LINE_RE.match(l) for l in input_lines] if m] if args.verbose: print >>sys.stderr, 'Found %d RUN lines:' % (len(run_lines),) for l in run_lines: print >>sys.stderr, ' RUN: ' + l - checks = [] + prefix_list = [] for l in run_lines: (llc_cmd, filecheck_cmd) = tuple([cmd.strip() for cmd in l.split('|', 1)]) if not llc_cmd.startswith('llc '): @@ -104,102 +177,65 @@ def main(): llc_cmd_args = llc_cmd_args.replace('< %s', '').replace('%s', '').strip() check_prefixes = [m.group(1) - for m in check_prefix_re.finditer(filecheck_cmd)] + for m in CHECK_PREFIX_RE.finditer(filecheck_cmd)] if not check_prefixes: check_prefixes = ['CHECK'] # FIXME: We should use multiple check prefixes to common check lines. For # now, we just ignore all but the last. - checks.append((check_prefixes, llc_cmd_args)) + prefix_list.append((check_prefixes, llc_cmd_args)) - asm = {} - for prefixes, _ in checks: + func_dict = {} + for prefixes, _ in prefix_list: for prefix in prefixes: - asm.update({prefix: dict()}) - for prefixes, llc_args in checks: + func_dict.update({prefix: dict()}) + for prefixes, llc_args in prefix_list: if args.verbose: print >>sys.stderr, 'Extracted LLC cmd: llc ' + llc_args print >>sys.stderr, 'Extracted FileCheck prefixes: ' + str(prefixes) - raw_asm = llc(args, llc_args, test) - # Build up a dictionary of all the function bodies. - for m in asm_function_re.finditer(raw_asm): - if not m: - continue - f = m.group('f') - f_asm = scrub_asm(m.group('body')) - if f.startswith('stress'): - # We only use the last line of the asm for stress tests. - f_asm = '\n'.join(f_asm.splitlines()[-1:]) - if args.verbose: - print >>sys.stderr, 'Processing asm for function: ' + f - for l in f_asm.splitlines(): - print >>sys.stderr, ' ' + l - for prefix in prefixes: - if f in asm[prefix] and asm[prefix][f] != f_asm: - if prefix == prefixes[-1]: - print >>sys.stderr, ('WARNING: Found conflicting asm under the ' - 'same prefix: %r!' % (prefix,)) - else: - asm[prefix][f] = None - continue - - asm[prefix][f] = f_asm + + raw_tool_output = llc(args, llc_args, test) + build_function_body_dictionary(raw_tool_output, prefixes, func_dict, args.verbose) is_in_function = False is_in_function_start = False - prefix_set = set([prefix for prefixes, _ in checks for prefix in prefixes]) + prefix_set = set([prefix for prefixes, _ in prefix_list for prefix in prefixes]) if args.verbose: print >>sys.stderr, 'Rewriting FileCheck prefixes: %s' % (prefix_set,) - fixed_lines = [] - fixed_lines.append(autogenerated_note) + output_lines = [] + output_lines.append(autogenerated_note) - for l in test_lines: + for input_line in input_lines: if is_in_function_start: - if l.lstrip().startswith(';'): - m = check_re.match(l) + if input_line == '': + continue + if input_line.lstrip().startswith(';'): + m = CHECK_RE.match(input_line) if not m or m.group(1) not in prefix_set: - fixed_lines.append(l) + output_lines.append(input_line) continue - # Print out the various check lines here - printed_prefixes = [] - for prefixes, _ in checks: - for prefix in prefixes: - if prefix in printed_prefixes: - break - if not asm[prefix][name]: - continue - if len(printed_prefixes) != 0: - fixed_lines.append(';') - printed_prefixes.append(prefix) - fixed_lines.append('; %s-LABEL: %s:' % (prefix, name)) - asm_lines = asm[prefix][name].splitlines() - fixed_lines.append('; %s: %s' % (prefix, asm_lines[0])) - for asm_line in asm_lines[1:]: - fixed_lines.append('; %s-NEXT: %s' % (prefix, asm_line)) - break + # Print out the various check lines here. + output_lines = add_checks(output_lines, prefix_list, func_dict, name) is_in_function_start = False if is_in_function: - # Skip any blank comment lines in the IR. - if l.strip() == ';': + if should_add_line_to_output(input_line, prefix_set) == True: + # This input line of the function body will go as-is into the output. + output_lines.append(input_line) + else: continue - # And skip any CHECK lines. We'll build our own. - m = check_re.match(l) - if m and m.group(1) in prefix_set: - continue - # Collect the remaining lines in the function body and look for the end - # of the function. - fixed_lines.append(l) - if l.strip() == '}': + if input_line.strip() == '}': is_in_function = False continue - if l == autogenerated_note: + if input_line == autogenerated_note: continue - fixed_lines.append(l) - m = ir_function_re.match(l) + # If it's outside a function, it just gets copied to the output. + output_lines.append(input_line) + + m = IR_FUNCTION_RE.match(input_line) if not m: continue name = m.group(1) @@ -209,10 +245,10 @@ def main(): is_in_function = is_in_function_start = True if args.verbose: - print>>sys.stderr, 'Writing %d fixed lines to %s...' % ( - len(fixed_lines), test) - with open(test, 'w') as f: - f.writelines([l + '\n' for l in fixed_lines]) + print>>sys.stderr, 'Writing %d lines to %s...' % (len(output_lines), test) + + with open(test, 'wb') as f: + f.writelines([l + '\n' for l in output_lines]) if __name__ == '__main__': diff --git a/utils/update_test_checks.py b/utils/update_test_checks.py new file mode 100755 index 0000000000000..c084debbe9863 --- /dev/null +++ b/utils/update_test_checks.py @@ -0,0 +1,397 @@ +#!/usr/bin/env python2.7 + +"""A script to generate FileCheck statements for regression tests. + +This script is a utility to update LLVM opt or llc test cases with new +FileCheck patterns. It can either update all of the tests in the file or +a single test function. + +Example usage: +$ update_test_checks.py --tool=../bin/opt test/foo.ll + +Workflow: +1. Make a compiler patch that requires updating some number of FileCheck lines + in regression test files. +2. Save the patch and revert it from your local work area. +3. Update the RUN-lines in the affected regression tests to look canonical. + Example: "; RUN: opt < %s -instcombine -S | FileCheck %s" +4. Refresh the FileCheck lines for either the entire file or select functions by + running this script. +5. Commit the fresh baseline of checks. +6. Apply your patch from step 1 and rebuild your local binaries. +7. Re-run this script on affected regression tests. +8. Check the diffs to ensure the script has done something reasonable. +9. Submit a patch including the regression test diffs for review. + +A common pattern is to have the script insert complete checking of every +instruction. Then, edit it down to only check the relevant instructions. +The script is designed to make adding checks to a test case fast, it is *not* +designed to be authoratitive about what constitutes a good test! +""" + +import argparse +import itertools +import os # Used to advertise this file's name ("autogenerated_note"). +import string +import subprocess +import sys +import tempfile +import re + +ADVERT = '; NOTE: Assertions have been autogenerated by ' + +# RegEx: this is where the magic happens. + +SCRUB_LEADING_WHITESPACE_RE = re.compile(r'^(\s+)') +SCRUB_WHITESPACE_RE = re.compile(r'(?!^(| \w))[ \t]+', flags=re.M) +SCRUB_TRAILING_WHITESPACE_RE = re.compile(r'[ \t]+$', flags=re.M) +SCRUB_X86_SHUFFLES_RE = ( + re.compile( + r'^(\s*\w+) [^#\n]+#+ ((?:[xyz]mm\d+|mem)( \{%k\d+\}( \{z\})?)? = .*)$', + flags=re.M)) +SCRUB_X86_SP_RE = re.compile(r'\d+\(%(esp|rsp)\)') +SCRUB_X86_RIP_RE = re.compile(r'[.\w]+\(%rip\)') +SCRUB_X86_LCP_RE = re.compile(r'\.LCPI[0-9]+_[0-9]+') +SCRUB_KILL_COMMENT_RE = re.compile(r'^ *#+ +kill:.*\n') +SCRUB_IR_COMMENT_RE = re.compile(r'\s*;.*') + +RUN_LINE_RE = re.compile('^\s*;\s*RUN:\s*(.*)$') +IR_FUNCTION_RE = re.compile('^\s*define\s+(?:internal\s+)?[^@]*@([\w-]+)\s*\(') +LLC_FUNCTION_RE = re.compile( + r'^_?(?P<func>[^:]+):[ \t]*#+[ \t]*@(?P=func)\n[^:]*?' + r'(?P<body>^##?[ \t]+[^:]+:.*?)\s*' + r'^\s*(?:[^:\n]+?:\s*\n\s*\.size|\.cfi_endproc|\.globl|\.comm|\.(?:sub)?section)', + flags=(re.M | re.S)) +OPT_FUNCTION_RE = re.compile( + r'^\s*define\s+(?:internal\s+)?[^@]*@(?P<func>[\w-]+?)\s*\(' + r'(\s+)?[^{]*\{\n(?P<body>.*?)\}', + flags=(re.M | re.S)) +CHECK_PREFIX_RE = re.compile('--check-prefix=(\S+)') +CHECK_RE = re.compile(r'^\s*;\s*([^:]+?)(?:-NEXT|-NOT|-DAG|-LABEL)?:') +IR_VALUE_DEF_RE = re.compile(r'\s+%(.*) =') + + +# Invoke the tool that is being tested. +def invoke_tool(args, cmd_args, ir): + with open(ir) as ir_file: + stdout = subprocess.check_output(args.tool_binary + ' ' + cmd_args, + shell=True, stdin=ir_file) + # Fix line endings to unix CR style. + stdout = stdout.replace('\r\n', '\n') + return stdout + + +# FIXME: Separate the x86-specific scrubbers, so this can be used for other targets. +def scrub_asm(asm): + # Detect shuffle asm comments and hide the operands in favor of the comments. + asm = SCRUB_X86_SHUFFLES_RE.sub(r'\1 {{.*#+}} \2', asm) + # Generically match the stack offset of a memory operand. + asm = SCRUB_X86_SP_RE.sub(r'{{[0-9]+}}(%\1)', asm) + # Generically match a RIP-relative memory operand. + asm = SCRUB_X86_RIP_RE.sub(r'{{.*}}(%rip)', asm) + # Generically match a LCP symbol. + asm = SCRUB_X86_LCP_RE.sub(r'{{\.LCPI.*}}', asm) + # Strip kill operands inserted into the asm. + asm = SCRUB_KILL_COMMENT_RE.sub('', asm) + return asm + + +def scrub_body(body, tool_basename): + # Scrub runs of whitespace out of the assembly, but leave the leading + # whitespace in place. + body = SCRUB_WHITESPACE_RE.sub(r' ', body) + # Expand the tabs used for indentation. + body = string.expandtabs(body, 2) + # Strip trailing whitespace. + body = SCRUB_TRAILING_WHITESPACE_RE.sub(r'', body) + if tool_basename == "llc": + body = scrub_asm(body) + return body + + +# Build up a dictionary of all the function bodies. +def build_function_body_dictionary(raw_tool_output, prefixes, func_dict, verbose, tool_basename): + if tool_basename == "llc": + func_regex = LLC_FUNCTION_RE + else: + func_regex = OPT_FUNCTION_RE + for m in func_regex.finditer(raw_tool_output): + if not m: + continue + func = m.group('func') + scrubbed_body = scrub_body(m.group('body'), tool_basename) + if func.startswith('stress'): + # We only use the last line of the function body for stress tests. + scrubbed_body = '\n'.join(scrubbed_body.splitlines()[-1:]) + if verbose: + print >>sys.stderr, 'Processing function: ' + func + for l in scrubbed_body.splitlines(): + print >>sys.stderr, ' ' + l + for prefix in prefixes: + if func in func_dict[prefix] and func_dict[prefix][func] != scrubbed_body: + if prefix == prefixes[-1]: + print >>sys.stderr, ('WARNING: Found conflicting asm under the ' + 'same prefix: %r!' % (prefix,)) + else: + func_dict[prefix][func] = None + continue + + func_dict[prefix][func] = scrubbed_body + + +# Create a FileCheck variable name based on an IR name. +def get_value_name(var): + if var.isdigit(): + var = 'TMP' + var + var = var.replace('.', '_') + return var.upper() + + +# Create a FileCheck variable from regex. +def get_value_definition(var): + return '[[' + get_value_name(var) + ':%.*]]' + + +# Use a FileCheck variable. +def get_value_use(var): + return '[[' + get_value_name(var) + ']]' + + +# Replace IR value defs and uses with FileCheck variables. +def genericize_check_lines(lines): + lines_with_def = [] + vars_seen = [] + for line in lines: + # An IR variable named '%.' matches the FileCheck regex string. + line = line.replace('%.', '%dot') + m = IR_VALUE_DEF_RE.match(line) + if m: + vars_seen.append(m.group(1)) + line = line.replace('%' + m.group(1), get_value_definition(m.group(1))) + + lines_with_def.append(line) + + # A single def isn't worth replacing? + #if len(vars_seen) < 2: + # return lines + + output_lines = [] + vars_seen.sort(key=len, reverse=True) + for line in lines_with_def: + for var in vars_seen: + line = line.replace('%' + var, get_value_use(var)) + output_lines.append(line) + + return output_lines + + +def add_checks(output_lines, prefix_list, func_dict, func_name, tool_basename): + # Select a label format based on the whether we're checking asm or IR. + if tool_basename == "llc": + check_label_format = "; %s-LABEL: %s:" + else: + check_label_format = "; %s-LABEL: @%s(" + + printed_prefixes = [] + for checkprefixes, _ in prefix_list: + for checkprefix in checkprefixes: + if checkprefix in printed_prefixes: + break + if not func_dict[checkprefix][func_name]: + continue + # Add some space between different check prefixes, but not after the last + # check line (before the test code). + #if len(printed_prefixes) != 0: + # output_lines.append(';') + printed_prefixes.append(checkprefix) + output_lines.append(check_label_format % (checkprefix, func_name)) + func_body = func_dict[checkprefix][func_name].splitlines() + + # For IR output, change all defs to FileCheck variables, so we're immune + # to variable naming fashions. + if tool_basename == "opt": + func_body = genericize_check_lines(func_body) + + # This could be selectively enabled with an optional invocation argument. + # Disabled for now: better to check everything. Be safe rather than sorry. + + # Handle the first line of the function body as a special case because + # it's often just noise (a useless asm comment or entry label). + #if func_body[0].startswith("#") or func_body[0].startswith("entry:"): + # is_blank_line = True + #else: + # output_lines.append('; %s: %s' % (checkprefix, func_body[0])) + # is_blank_line = False + + # For llc tests, there may be asm directives between the label and the + # first checked line (most likely that first checked line is "# BB#0"). + if tool_basename == "opt": + is_blank_line = False + else: + is_blank_line = True; + + for func_line in func_body: + if func_line.strip() == '': + is_blank_line = True + continue + # Do not waste time checking IR comments. + if tool_basename == "opt": + func_line = SCRUB_IR_COMMENT_RE.sub(r'', func_line) + + # Skip blank lines instead of checking them. + if is_blank_line == True: + output_lines.append('; %s: %s' % (checkprefix, func_line)) + else: + output_lines.append('; %s-NEXT: %s' % (checkprefix, func_line)) + is_blank_line = False + + # Add space between different check prefixes and also before the first + # line of code in the test function. + output_lines.append(';') + break + return output_lines + + +def should_add_line_to_output(input_line, prefix_set): + # Skip any blank comment lines in the IR. + if input_line.strip() == ';': + return False + # Skip any blank lines in the IR. + #if input_line.strip() == '': + # return False + # And skip any CHECK lines. We're building our own. + m = CHECK_RE.match(input_line) + if m and m.group(1) in prefix_set: + return False + + return True + + +def main(): + from argparse import RawTextHelpFormatter + parser = argparse.ArgumentParser(description=__doc__, formatter_class=RawTextHelpFormatter) + parser.add_argument('-v', '--verbose', action='store_true', + help='Show verbose output') + parser.add_argument('--tool-binary', default='llc', + help='The tool used to generate the test case') + parser.add_argument( + '--function', help='The function in the test file to update') + parser.add_argument('tests', nargs='+') + args = parser.parse_args() + + autogenerated_note = (ADVERT + 'utils/' + os.path.basename(__file__)) + + tool_basename = os.path.basename(args.tool_binary) + if (tool_basename != "llc" and tool_basename != "opt"): + print >>sys.stderr, 'ERROR: Unexpected tool name: ' + tool_basename + sys.exit(1) + + for test in args.tests: + if args.verbose: + print >>sys.stderr, 'Scanning for RUN lines in test file: %s' % (test,) + with open(test) as f: + input_lines = [l.rstrip() for l in f] + + run_lines = [m.group(1) + for m in [RUN_LINE_RE.match(l) for l in input_lines] if m] + if args.verbose: + print >>sys.stderr, 'Found %d RUN lines:' % (len(run_lines),) + for l in run_lines: + print >>sys.stderr, ' RUN: ' + l + + prefix_list = [] + for l in run_lines: + (tool_cmd, filecheck_cmd) = tuple([cmd.strip() for cmd in l.split('|', 1)]) + + if not tool_cmd.startswith(tool_basename + ' '): + print >>sys.stderr, 'WARNING: Skipping non-%s RUN line: %s' % (tool_basename, l) + continue + + if not filecheck_cmd.startswith('FileCheck '): + print >>sys.stderr, 'WARNING: Skipping non-FileChecked RUN line: ' + l + continue + + tool_cmd_args = tool_cmd[len(tool_basename):].strip() + tool_cmd_args = tool_cmd_args.replace('< %s', '').replace('%s', '').strip() + + check_prefixes = [m.group(1) + for m in CHECK_PREFIX_RE.finditer(filecheck_cmd)] + if not check_prefixes: + check_prefixes = ['CHECK'] + + # FIXME: We should use multiple check prefixes to common check lines. For + # now, we just ignore all but the last. + prefix_list.append((check_prefixes, tool_cmd_args)) + + func_dict = {} + for prefixes, _ in prefix_list: + for prefix in prefixes: + func_dict.update({prefix: dict()}) + for prefixes, tool_args in prefix_list: + if args.verbose: + print >>sys.stderr, 'Extracted tool cmd: ' + tool_basename + ' ' + tool_args + print >>sys.stderr, 'Extracted FileCheck prefixes: ' + str(prefixes) + + raw_tool_output = invoke_tool(args, tool_args, test) + build_function_body_dictionary(raw_tool_output, prefixes, func_dict, args.verbose, tool_basename) + + is_in_function = False + is_in_function_start = False + prefix_set = set([prefix for prefixes, _ in prefix_list for prefix in prefixes]) + if args.verbose: + print >>sys.stderr, 'Rewriting FileCheck prefixes: %s' % (prefix_set,) + output_lines = [] + output_lines.append(autogenerated_note) + + for input_line in input_lines: + if is_in_function_start: + if input_line == '': + continue + if input_line.lstrip().startswith(';'): + m = CHECK_RE.match(input_line) + if not m or m.group(1) not in prefix_set: + output_lines.append(input_line) + continue + + # Print out the various check lines here. + output_lines = add_checks(output_lines, prefix_list, func_dict, name, tool_basename) + is_in_function_start = False + + if is_in_function: + if should_add_line_to_output(input_line, prefix_set) == True: + # This input line of the function body will go as-is into the output. + # Except make leading whitespace uniform: 2 spaces. + input_line = SCRUB_LEADING_WHITESPACE_RE.sub(r' ', input_line) + output_lines.append(input_line) + else: + continue + if input_line.strip() == '}': + is_in_function = False + continue + + # Discard any previous script advertising. + if input_line.startswith(ADVERT): + continue + + # If it's outside a function, it just gets copied to the output. + output_lines.append(input_line) + + m = IR_FUNCTION_RE.match(input_line) + if not m: + continue + name = m.group(1) + if args.function is not None and name != args.function: + # When filtering on a specific function, skip all others. + continue + is_in_function = is_in_function_start = True + + if args.verbose: + print>>sys.stderr, 'Writing %d lines to %s...' % (len(output_lines), test) + + with open(test, 'wb') as f: + f.writelines([l + '\n' for l in output_lines]) + + +if __name__ == '__main__': + main() + diff --git a/utils/vim/syntax/llvm.vim b/utils/vim/syntax/llvm.vim index 68a53ec2383d9..8f6829dc9e9fc 100644 --- a/utils/vim/syntax/llvm.vim +++ b/utils/vim/syntax/llvm.vim @@ -1,7 +1,7 @@ " Vim syntax file " Language: llvm " Maintainer: The LLVM team, http://llvm.org/ -" Version: $Revision: 256512 $ +" Version: $Revision: 275248 $ if version < 600 syntax clear @@ -36,28 +36,122 @@ syn keyword llvmStatement umax umin une uno unreachable unwind urem va_arg syn keyword llvmStatement xchg xor zext " Keywords. -syn keyword llvmKeyword acq_rel acquire sanitize_address addrspace alias align -syn keyword llvmKeyword alignstack alwaysinline appending arm_aapcs_vfpcc -syn keyword llvmKeyword arm_aapcscc arm_apcscc asm atomic available_externally -syn keyword llvmKeyword blockaddress byval c catch cc ccc cleanup coldcc common -syn keyword llvmKeyword constant datalayout declare default define deplibs -syn keyword llvmKeyword distinct dllexport dllimport except extern_weak external -syn keyword llvmKeyword externally_initialized fastcc filter gc global hhvmcc -syn keyword llvmKeyword hhvm_ccc hidden initialexec inlinehint inreg -syn keyword llvmKeyword intel_ocl_bicc inteldialect internal linkonce -syn keyword llvmKeyword linkonce_odr localdynamic localexec minsize module -syn keyword llvmKeyword monotonic msp430_intrcc musttail naked nest -syn keyword llvmKeyword noalias nocapture noimplicitfloat noinline nonlazybind -syn keyword llvmKeyword noredzone noreturn nounwind optnone optsize personality -syn keyword llvmKeyword private protected ptx_device ptx_kernel readnone -syn keyword llvmKeyword readonly release returns_twice sanitize_thread -syn keyword llvmKeyword sanitize_memory section seq_cst sideeffect signext -syn keyword llvmKeyword singlethread spir_func spir_kernel sret ssp sspreq -syn keyword llvmKeyword sspstrong tail target thread_local to triple -syn keyword llvmKeyword unnamed_addr unordered uwtable volatile weak weak_odr -syn keyword llvmKeyword x86_fastcallcc x86_stdcallcc x86_thiscallcc -syn keyword llvmKeyword x86_64_sysvcc x86_64_win64cc zeroext uselistorder -syn keyword llvmKeyword uselistorder_bb musttail +syn keyword llvmKeyword + \ acq_rel + \ acquire + \ addrspace + \ alias + \ align + \ alignstack + \ alwaysinline + \ appending + \ arm_aapcscc + \ arm_aapcs_vfpcc + \ arm_apcscc + \ asm + \ atomic + \ available_externally + \ blockaddress + \ byval + \ c + \ catch + \ cc + \ ccc + \ cleanup + \ coldcc + \ common + \ constant + \ datalayout + \ declare + \ default + \ define + \ deplibs + \ distinct + \ dllexport + \ dllimport + \ except + \ external + \ externally_initialized + \ extern_weak + \ fastcc + \ filter + \ gc + \ global + \ hhvmcc + \ hhvm_ccc + \ hidden + \ initialexec + \ inlinehint + \ inreg + \ inteldialect + \ intel_ocl_bicc + \ internal + \ linkonce + \ linkonce_odr + \ localdynamic + \ localexec + \ local_unnamed_addr + \ minsize + \ module + \ monotonic + \ msp430_intrcc + \ musttail + \ naked + \ nest + \ noalias + \ nocapture + \ noimplicitfloat + \ noinline + \ nonlazybind + \ noredzone + \ noreturn + \ nounwind + \ optnone + \ optsize + \ personality + \ private + \ protected + \ ptx_device + \ ptx_kernel + \ readnone + \ readonly + \ release + \ returns_twice + \ sanitize_address + \ sanitize_memory + \ sanitize_thread + \ section + \ seq_cst + \ sideeffect + \ signext + \ singlethread + \ source_filename + \ spir_func + \ spir_kernel + \ sret + \ ssp + \ sspreq + \ sspstrong + \ swiftcc + \ tail + \ target + \ thread_local + \ to + \ triple + \ unnamed_addr + \ unordered + \ uselistorder + \ uselistorder_bb + \ uwtable + \ volatile + \ weak + \ weak_odr + \ x86_64_sysvcc + \ x86_64_win64cc + \ x86_fastcallcc + \ x86_stdcallcc + \ x86_thiscallcc + \ zeroext " Obsolete keywords. syn keyword llvmError getresult begin end diff --git a/utils/yaml-bench/Makefile b/utils/yaml-bench/Makefile deleted file mode 100644 index 07e91226c7a99..0000000000000 --- a/utils/yaml-bench/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -##===- utils/yaml-bench/Makefile ---------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../.. -TOOLNAME = yaml-bench -USEDLIBS = LLVMSupport.a - -# This tool has no plugins, optimize startup time. -TOOL_NO_EXPORTS = 1 - -# Don't install this utility -NO_INSTALL = 1 - -include $(LEVEL)/Makefile.common |