diff options
Diffstat (limited to 'utils')
103 files changed, 6379 insertions, 5437 deletions
diff --git a/utils/FileCheck/FileCheck.cpp b/utils/FileCheck/FileCheck.cpp index f225594..33f04ce 100644 --- a/utils/FileCheck/FileCheck.cpp +++ b/utils/FileCheck/FileCheck.cpp @@ -117,8 +117,9 @@ bool Pattern::ParsePattern(StringRef PatternStr, SourceMgr &SM) { // Check that there is something on the line. if (PatternStr.empty()) { - SM.PrintMessage(PatternLoc, "found empty check string with prefix '" + - CheckPrefix+":'", "error"); + SM.PrintMessage(PatternLoc, SourceMgr::DK_Error, + "found empty check string with prefix '" + + CheckPrefix+":'"); return true; } @@ -144,7 +145,8 @@ bool Pattern::ParsePattern(StringRef PatternStr, SourceMgr &SM) { size_t End = PatternStr.find("}}"); if (End == StringRef::npos) { SM.PrintMessage(SMLoc::getFromPointer(PatternStr.data()), - "found start of regex string with no end '}}'","error"); + SourceMgr::DK_Error, + "found start of regex string with no end '}}'"); return true; } @@ -173,7 +175,8 @@ bool Pattern::ParsePattern(StringRef PatternStr, SourceMgr &SM) { size_t End = PatternStr.find("]]"); if (End == StringRef::npos) { SM.PrintMessage(SMLoc::getFromPointer(PatternStr.data()), - "invalid named regex reference, no ]] found", "error"); + SourceMgr::DK_Error, + "invalid named regex reference, no ]] found"); return true; } @@ -185,8 +188,8 @@ bool Pattern::ParsePattern(StringRef PatternStr, SourceMgr &SM) { StringRef Name = MatchStr.substr(0, NameEnd); if (Name.empty()) { - SM.PrintMessage(SMLoc::getFromPointer(Name.data()), - "invalid name in named regex: empty name", "error"); + SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, + "invalid name in named regex: empty name"); return true; } @@ -194,14 +197,14 @@ bool Pattern::ParsePattern(StringRef PatternStr, SourceMgr &SM) { for (unsigned i = 0, e = Name.size(); i != e; ++i) if (Name[i] != '_' && !isalnum(Name[i])) { SM.PrintMessage(SMLoc::getFromPointer(Name.data()+i), - "invalid name in named regex", "error"); + SourceMgr::DK_Error, "invalid name in named regex"); return true; } // Name can't start with a digit. if (isdigit(Name[0])) { - SM.PrintMessage(SMLoc::getFromPointer(Name.data()), - "invalid name in named regex", "error"); + SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, + "invalid name in named regex"); return true; } @@ -266,8 +269,8 @@ bool Pattern::AddRegExToRegEx(StringRef RegexStr, unsigned &CurParen, Regex R(RegexStr); std::string Error; if (!R.isValid(Error)) { - SM.PrintMessage(SMLoc::getFromPointer(RegexStr.data()), - "invalid regex: " + Error, "error"); + SM.PrintMessage(SMLoc::getFromPointer(RegexStr.data()), SourceMgr::DK_Error, + "invalid regex: " + Error); return true; } @@ -383,8 +386,8 @@ void Pattern::PrintFailureInfo(const SourceMgr &SM, StringRef Buffer, OS.write_escaped(it->second) << "\""; } - SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), OS.str(), "note", - /*ShowLine=*/false); + SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note, + OS.str()); } } @@ -422,7 +425,7 @@ void Pattern::PrintFailureInfo(const SourceMgr &SM, StringRef Buffer, // line. if (Best && Best != StringRef::npos && BestQuality < 50) { SM.PrintMessage(SMLoc::getFromPointer(Buffer.data() + Best), - "possible intended match here", "note"); + SourceMgr::DK_Note, "possible intended match here"); // FIXME: If we wanted to be really friendly we would show why the match // failed, as it can be hard to spot simple one character differences. @@ -566,8 +569,9 @@ static bool ReadCheckFile(SourceMgr &SM, // Verify that CHECK-NEXT lines have at least one CHECK line before them. if (IsCheckNext && CheckStrings.empty()) { SM.PrintMessage(SMLoc::getFromPointer(CheckPrefixStart), + SourceMgr::DK_Error, "found '"+CheckPrefix+"-NEXT:' without previous '"+ - CheckPrefix+ ": line", "error"); + CheckPrefix+ ": line"); return true; } @@ -607,15 +611,15 @@ static void PrintCheckFailed(const SourceMgr &SM, const CheckString &CheckStr, StringRef Buffer, StringMap<StringRef> &VariableTable) { // Otherwise, we have an error, emit an error message. - SM.PrintMessage(CheckStr.Loc, "expected string not found in input", - "error"); + SM.PrintMessage(CheckStr.Loc, SourceMgr::DK_Error, + "expected string not found in input"); // Print the "scanning from here" line. If the current position is at the // end of a line, advance to the start of the next line. Buffer = Buffer.substr(Buffer.find_first_not_of(" \t\n\r")); - SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), "scanning from here", - "note"); + SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note, + "scanning from here"); // Allow the pattern to print additional information if desired. CheckStr.Pat.PrintFailureInfo(SM, Buffer, VariableTable); @@ -710,25 +714,22 @@ int main(int argc, char **argv) { unsigned NumNewLines = CountNumNewlinesBetween(SkippedRegion); if (NumNewLines == 0) { - SM.PrintMessage(CheckStr.Loc, - CheckPrefix+"-NEXT: is on the same line as previous match", - "error"); + SM.PrintMessage(CheckStr.Loc, SourceMgr::DK_Error, + CheckPrefix+"-NEXT: is on the same line as previous match"); SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), - "'next' match was here", "note"); - SM.PrintMessage(SMLoc::getFromPointer(LastMatch), - "previous match was here", "note"); + SourceMgr::DK_Note, "'next' match was here"); + SM.PrintMessage(SMLoc::getFromPointer(LastMatch), SourceMgr::DK_Note, + "previous match was here"); return 1; } if (NumNewLines != 1) { - SM.PrintMessage(CheckStr.Loc, - CheckPrefix+ - "-NEXT: is not on the line after the previous match", - "error"); + SM.PrintMessage(CheckStr.Loc, SourceMgr::DK_Error, CheckPrefix+ + "-NEXT: is not on the line after the previous match"); SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), - "'next' match was here", "note"); - SM.PrintMessage(SMLoc::getFromPointer(LastMatch), - "previous match was here", "note"); + SourceMgr::DK_Note, "'next' match was here"); + SM.PrintMessage(SMLoc::getFromPointer(LastMatch), SourceMgr::DK_Note, + "previous match was here"); return 1; } } @@ -743,10 +744,10 @@ int main(int argc, char **argv) { VariableTable); if (Pos == StringRef::npos) continue; - SM.PrintMessage(SMLoc::getFromPointer(LastMatch+Pos), - CheckPrefix+"-NOT: string occurred!", "error"); - SM.PrintMessage(CheckStr.NotStrings[ChunkNo].first, - CheckPrefix+"-NOT: pattern specified here", "note"); + SM.PrintMessage(SMLoc::getFromPointer(LastMatch+Pos), SourceMgr::DK_Error, + CheckPrefix+"-NOT: string occurred!"); + SM.PrintMessage(CheckStr.NotStrings[ChunkNo].first, SourceMgr::DK_Note, + CheckPrefix+"-NOT: pattern specified here"); return 1; } diff --git a/utils/GenLibDeps.pl b/utils/GenLibDeps.pl index 0cd9e6a..656250c 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/^CBackend/Target\/CBackend/; $libpath =~ s/^CppBackend/Target\/CppBackend/; $libpath =~ s/^MSIL/Target\/MSIL/; $libpath =~ s/^Core/VMCore/; @@ -138,7 +137,6 @@ if ($PEROBJ) { $libpath =~ s/^AsmPrinter/CodeGen\/AsmPrinter/; $libpath =~ s/^BitReader/Bitcode\/Reader/; $libpath =~ s/^BitWriter/Bitcode\/Writer/; - $libpath =~ s/^CBackend/Target\/CBackend/; $libpath =~ s/^CppBackend/Target\/CppBackend/; $libpath =~ s/^MSIL/Target\/MSIL/; $libpath =~ s/^Core/VMCore/; diff --git a/utils/KillTheDoctor/KillTheDoctor.cpp b/utils/KillTheDoctor/KillTheDoctor.cpp index 1ddae0b..70713b2 100644 --- a/utils/KillTheDoctor/KillTheDoctor.cpp +++ b/utils/KillTheDoctor/KillTheDoctor.cpp @@ -211,19 +211,6 @@ static error_code GetFileNameFromHandle(HANDLE FileHandle, } } -static std::string QuoteProgramPathIfNeeded(StringRef Command) { - if (Command.find_first_of(' ') == StringRef::npos) - return Command; - else { - std::string ret; - ret.reserve(Command.size() + 3); - ret.push_back('"'); - ret.append(Command.begin(), Command.end()); - ret.push_back('"'); - return ret; - } -} - /// @brief Find program using shell lookup rules. /// @param Program This is either an absolute path, relative path, or simple a /// program name. Look in PATH for any programs that match. If no @@ -269,39 +256,6 @@ static std::string FindProgram(const std::string &Program, error_code &ec) { return PathName; } -static error_code EnableDebugPrivileges() { - HANDLE TokenHandle; - BOOL success = ::OpenProcessToken(::GetCurrentProcess(), - TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, - &TokenHandle); - if (!success) - return windows_error(::GetLastError()); - - TokenScopedHandle Token(TokenHandle); - TOKEN_PRIVILEGES TokenPrivileges; - LUID LocallyUniqueID; - - success = ::LookupPrivilegeValueA(NULL, - SE_DEBUG_NAME, - &LocallyUniqueID); - if (!success) - return windows_error(::GetLastError()); - - TokenPrivileges.PrivilegeCount = 1; - TokenPrivileges.Privileges[0].Luid = LocallyUniqueID; - TokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - - success = ::AdjustTokenPrivileges(Token, - FALSE, - &TokenPrivileges, - sizeof(TOKEN_PRIVILEGES), - NULL, - NULL); - // The value of success is basically useless. Either way we are just returning - // the value of ::GetLastError(). - return windows_error(::GetLastError()); -} - static StringRef ExceptionCodeToString(DWORD ExceptionCode) { switch(ExceptionCode) { case EXCEPTION_ACCESS_VIOLATION: return "EXCEPTION_ACCESS_VIOLATION"; diff --git a/utils/LLVMBuild.txt b/utils/LLVMBuild.txt new file mode 100644 index 0000000..382bfd3 --- /dev/null +++ b/utils/LLVMBuild.txt @@ -0,0 +1,29 @@ +;===- ./utils/LLVMBuild.txt ------------------------------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[common] +subdirectories = TableGen unittest + +[component_0] +type = Group +name = BuildTools +parent = $ROOT + +[component_1] +type = Group +name = UtilityTools +parent = $ROOT diff --git a/utils/Makefile b/utils/Makefile index 9d4dc5c2..b983760 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -9,14 +9,11 @@ LEVEL = .. PARALLEL_DIRS := FileCheck FileUpdate TableGen PerfectShuffle \ - count fpcmp llvm-lit not unittest + count fpcmp llvm-lit not unittest json-bench -EXTRA_DIST := cgiplotNLT.pl check-each-file codegen-diff countloc.sh \ +EXTRA_DIST := check-each-file codegen-diff countloc.sh \ DSAclean.py DSAextract.py emacs findsym.pl GenLibDeps.pl \ - getsrcs.sh importNLT.pl llvmdo llvmgrep llvm-native-gcc \ - llvm-native-gxx makellvm NightlyTest.gnuplot NightlyTest.pl \ - NightlyTestTemplate.html NLT.schema \ - parseNLT.pl plotNLT.pl profile.pl \ - webNLT.pl vim + getsrcs.sh llvmdo llvmgrep llvm-native-gcc \ + llvm-native-gxx makellvm profile.pl vim include $(LEVEL)/Makefile.common diff --git a/utils/NLT.schema b/utils/NLT.schema deleted file mode 100644 index 4bcddbc..0000000 --- a/utils/NLT.schema +++ /dev/null @@ -1,8 +0,0 @@ -CREATE TABLE `Tests` ( - `NAME` varchar(255) NOT NULL default '', - `RUN` date NOT NULL default '0000-00-00', - `TEST` varchar(32) NOT NULL default '', - `VALUE` double NOT NULL default '0', - KEY `name_index` (`NAME`) -) ENGINE=MyISAM DEFAULT CHARSET=latin1 - diff --git a/utils/NewNightlyTest.pl b/utils/NewNightlyTest.pl deleted file mode 100755 index da806e9..0000000 --- a/utils/NewNightlyTest.pl +++ /dev/null @@ -1,836 +0,0 @@ -#!/usr/bin/perl -use POSIX qw(strftime); -use File::Copy; -use File::Find; -use Socket; - -# -# Program: NewNightlyTest.pl -# -# Synopsis: Perform a series of tests which are designed to be run nightly. -# This is used to keep track of the status of the LLVM tree, tracking -# regressions and performance changes. Submits this information -# to llvm.org where it is placed into the nightlytestresults database. -# -# Syntax: NightlyTest.pl [OPTIONS] [CVSROOT BUILDDIR WEBDIR] -# where -# OPTIONS may include one or more of the following: -# -# MAIN OPTIONS: -# -config LLVMPATH If specified, use an existing LLVM build and only run and -# report the test information. The LLVMCONFIG argument should -# be the path to the llvm-config executable in the LLVM build. -# This should be the first argument if given. NOT YET -# IMPLEMENTED. -# -nickname NAME The NAME argument specifieds the nickname this script -# will submit to the nightlytest results repository. -# -nouname Don't include uname data (machine will be identified by nickname only). -# -submit-server Specifies a server to submit the test results too. If this -# option is not specified it defaults to -# llvm.org. This is basically just the address of the -# webserver -# -submit-script Specifies which script to call on the submit server. If -# this option is not specified it defaults to -# /nightlytest/NightlyTestAccept.php. This is basically -# everything after the www.yourserver.org. -# -submit-aux If specified, an auxiliary script to run in addition to the -# normal submit script. The script will be passed the path to -# the "sentdata.txt" file as its sole argument. -# -nosubmit Do not report the test results back to a submit server. -# -# -# BUILD OPTIONS (not used with -config): -# -nocheckout Do not create, checkout, update, or configure -# the source tree. -# -noremove Do not remove the BUILDDIR after it has been built. -# -noremoveresults Do not remove the WEBDIR after it has been built. -# -noclean Do not run 'make clean' before building. -# -nobuild Do not build llvm. If tests are enabled perform them -# on the llvm build specified in the build directory -# -release Build an LLVM Release+Asserts version -# -release-asserts Build an LLVM Release version -# -disable-bindings Disable building LLVM bindings. -# -with-clang Checkout Clang source into tools/clang. -# -compileflags Next argument specifies extra options passed to make when -# building LLVM. -# -use-gmake Use gmake instead of the default make command to build -# llvm and run tests. -# -llvmgccdir Next argument specifies the llvm-gcc install prefix. -# -# TESTING OPTIONS: -# -notest Do not even attempt to run the test programs. -# -nodejagnu Do not run feature or regression tests -# -enable-llcbeta Enable testing of beta features in llc. -# -enable-lli Enable testing of lli (interpreter) features, default is off -# -disable-pic Disable building with Position Independent Code. -# -disable-llc Disable LLC tests in the nightly tester. -# -disable-jit Disable JIT tests in the nightly tester. -# -disable-cbe Disable C backend tests in the nightly tester. -# -disable-lto Disable link time optimization. -# -test-cflags Next argument specifies that C compilation options that -# override the default when running the testsuite. -# -test-cxxflags Next argument specifies that C++ compilation options that -# override the default when running the testsuite. -# -extraflags Next argument specifies extra options that are passed to -# compile the tests. -# -noexternals Do not run the external tests (for cases where povray -# or SPEC are not installed) -# -with-externals Specify a directory where the external tests are located. -# -# OTHER OPTIONS: -# -parallel Run parallel jobs with GNU Make (see -parallel-jobs). -# -parallel-jobs The number of parallel Make jobs to use (default is two). -# -parallel-test Allow parallel execution of llvm-test -# -verbose Turn on some debug output -# -nice Checkout/Configure/Build with "nice" to reduce impact -# on busy servers. -# -f2c Next argument specifies path to F2C utility -# -gccpath Path to gcc/g++ used to build LLVM -# -target Specify the target triplet -# -cflags Next argument specifies that C compilation options that -# override the default. -# -cxxflags Next argument specifies that C++ compilation options that -# override the default. -# -ldflags Next argument specifies that linker options that override -# the default. -# -# CVSROOT is ignored, it is passed for backwards compatibility. -# BUILDDIR is the directory where sources for this test run will be checked out -# AND objects for this test run will be built. This directory MUST NOT -# exist before the script is run; it will be created by the svn checkout -# process and erased (unless -noremove is specified; see above.) -# WEBDIR is the directory into which the test results web page will be written, -# AND in which the "index.html" is assumed to be a symlink to the most recent -# copy of the results. This directory will be created if it does not exist. -# LLVMGCCDIR is the directory in which the LLVM GCC Front End is installed -# to. This is the same as you would have for a normal LLVM build. -# -############################################################## -# -# Getting environment variables -# -############################################################## -my $HOME = $ENV{'HOME'}; -my $SVNURL = $ENV{"SVNURL"}; -$SVNURL = 'http://llvm.org/svn/llvm-project' unless $SVNURL; -my $TestSVNURL = $ENV{"TestSVNURL"}; -$TestSVNURL = 'http://llvm.org/svn/llvm-project' unless $TestSVNURL; -my $BuildDir = $ENV{'BUILDDIR'}; -my $WebDir = $ENV{'WEBDIR'}; - -############################################################## -# -# Calculate the date prefix... -# -############################################################## -use POSIX; -@TIME = localtime; -my $DATE = strftime("%Y-%m-%d_%H-%M-%S", localtime()); - -############################################################## -# -# Parse arguments... -# -############################################################## -$CONFIG_PATH=""; -$CONFIGUREARGS=""; -$nickname=""; -$NOTEST=0; -$MAKECMD="make"; -$SUBMITSERVER = "llvm.org"; -$SUBMITSCRIPT = "/nightlytest/NightlyTestAccept.php"; -$SUBMITAUX=""; -$SUBMIT = 1; -$PARALLELJOBS = "2"; -my $TESTFLAGS=""; - -if ($ENV{'LLVMGCCDIR'}) { - $CONFIGUREARGS .= " --with-llvmgccdir=" . $ENV{'LLVMGCCDIR'}; - $LLVMGCCPATH = $ENV{'LLVMGCCDIR'} . '/bin'; -} -else { - $LLVMGCCPATH = ""; -} - -while (scalar(@ARGV) and ($_ = $ARGV[0], /^[-+]/)) { - shift; - last if /^--$/; # Stop processing arguments on -- - - # List command line options here... - if (/^-config$/) { $CONFIG_PATH = "$ARGV[0]"; shift; next; } - if (/^-nocheckout$/) { $NOCHECKOUT = 1; next; } - if (/^-noclean$/) { $NOCLEAN = 1; next; } - if (/^-noremove$/) { $NOREMOVE = 1; next; } - if (/^-noremoveatend$/) { $NOREMOVEATEND = 1; next; } - if (/^-noremoveresults$/){ $NOREMOVERESULTS = 1; next; } - if (/^-notest$/) { $NOTEST = 1; next; } - if (/^-norunningtests$/) { next; } # Backward compatibility, ignored. - if (/^-parallel-jobs$/) { $PARALLELJOBS = "$ARGV[0]"; shift; next;} - if (/^-parallel$/) { $MAKEOPTS = "$MAKEOPTS -j$PARALLELJOBS"; next; } - if (/^-parallel-test$/) { $PROGTESTOPTS .= " ENABLE_PARALLEL_REPORT=1"; next; } - if (/^-with-clang$/) { $WITHCLANG = 1; next; } - if (/^-release$/) { $MAKEOPTS = "$MAKEOPTS ENABLE_OPTIMIZED=1 ". - "OPTIMIZE_OPTION=-O2"; next;} - if (/^-release-asserts$/){ $MAKEOPTS = "$MAKEOPTS ENABLE_OPTIMIZED=1 ". - "DISABLE_ASSERTIONS=1 ". - "OPTIMIZE_OPTION=-O2"; next;} - if (/^-enable-llcbeta$/) { $PROGTESTOPTS .= " ENABLE_LLCBETA=1"; next; } - if (/^-disable-pic$/) { $CONFIGUREARGS .= " --enable-pic=no"; next; } - if (/^-enable-lli$/) { $PROGTESTOPTS .= " ENABLE_LLI=1"; - $CONFIGUREARGS .= " --enable-lli"; next; } - if (/^-disable-llc$/) { $PROGTESTOPTS .= " DISABLE_LLC=1"; - $CONFIGUREARGS .= " --disable-llc_diffs"; next; } - if (/^-disable-jit$/) { $PROGTESTOPTS .= " DISABLE_JIT=1"; - $CONFIGUREARGS .= " --disable-jit"; next; } - if (/^-disable-bindings$/) { $CONFIGUREARGS .= " --disable-bindings"; next; } - if (/^-disable-cbe$/) { $PROGTESTOPTS .= " DISABLE_CBE=1"; next; } - if (/^-disable-lto$/) { $PROGTESTOPTS .= " DISABLE_LTO=1"; next; } - if (/^-test-opts$/) { $PROGTESTOPTS .= " $ARGV[0]"; shift; next; } - if (/^-verbose$/) { $VERBOSE = 1; next; } - if (/^-teelogs$/) { $TEELOGS = 1; next; } - if (/^-nice$/) { $NICE = "nice "; next; } - if (/^-f2c$/) { $CONFIGUREARGS .= " --with-f2c=$ARGV[0]"; - shift; next; } - if (/^-with-externals$/) { $CONFIGUREARGS .= " --with-externals=$ARGV[0]"; - shift; next; } - if (/^-configure-args$/) { $CONFIGUREARGS .= " $ARGV[0]"; - shift; next; } - if (/^-submit-server/) { $SUBMITSERVER = "$ARGV[0]"; shift; next; } - if (/^-submit-script/) { $SUBMITSCRIPT = "$ARGV[0]"; shift; next; } - if (/^-submit-aux/) { $SUBMITAUX = "$ARGV[0]"; shift; next; } - if (/^-nosubmit$/) { $SUBMIT = 0; next; } - if (/^-nickname$/) { $nickname = "$ARGV[0]"; shift; next; } - if (/^-gccpath/) { $CONFIGUREARGS .= - " CC=$ARGV[0]/gcc CXX=$ARGV[0]/g++"; - $GCCPATH=$ARGV[0]; shift; next; } - else { $GCCPATH=""; } - if (/^-target/) { $CONFIGUREARGS .= " --target=$ARGV[0]"; - shift; next; } - if (/^-cflags/) { $MAKEOPTS = "$MAKEOPTS C.Flags=\'$ARGV[0]\'"; - shift; next; } - if (/^-cxxflags/) { $MAKEOPTS = "$MAKEOPTS CXX.Flags=\'$ARGV[0]\'"; - shift; next; } - if (/^-ldflags/) { $MAKEOPTS = "$MAKEOPTS LD.Flags=\'$ARGV[0]\'"; - shift; next; } - if (/^-test-cflags/) { $TESTFLAGS = "$TESTFLAGS CFLAGS=\'$ARGV[0]\'"; - shift; next; } - if (/^-test-cxxflags/) { $TESTFLAGS = "$TESTFLAGS CXXFLAGS=\'$ARGV[0]\'"; - shift; next; } - if (/^-compileflags/) { $MAKEOPTS = "$MAKEOPTS $ARGV[0]"; shift; next; } - if (/^-llvmgccdir/) { $CONFIGUREARGS .= " --with-llvmgccdir=\'$ARGV[0]\'"; - $LLVMGCCPATH = $ARGV[0] . '/bin'; - shift; next;} - if (/^-noexternals$/) { $NOEXTERNALS = 1; next; } - if (/^-nouname$/) { $NOUNAME = 1; next; } - if (/^-use-gmake/) { $MAKECMD = "gmake"; shift; next; } - if (/^-extraflags/) { $CONFIGUREARGS .= - " --with-extra-options=\'$ARGV[0]\'"; shift; next;} - if (/^-noexternals$/) { $NOEXTERNALS = 1; next; } - if (/^-nodejagnu$/) { next; } - if (/^-nobuild$/) { $NOBUILD = 1; next; } - print "Unknown option: $_ : ignoring!\n"; -} - -if ($CONFIGUREARGS !~ /--disable-jit/) { - $CONFIGUREARGS .= " --enable-jit"; -} - -if (@ARGV != 0 and @ARGV != 3) { - die "error: must specify 0 or 3 options!"; -} - -if (@ARGV == 3) { - if ($CONFIG_PATH ne "") { - die "error: arguments are unsupported in -config mode,"; - } - - # ARGV[0] used to be the CVS root, ignored for backward compatibility. - $BuildDir = $ARGV[1]; - $WebDir = $ARGV[2]; -} - -if ($CONFIG_PATH ne "") { - $BuildDir = ""; - $SVNURL = $TestSVNURL = ""; - if ($WebDir eq "") { - die("please specify a web directory"); - } -} else { - if ($BuildDir eq "" or - $WebDir eq "") { - die("please specify a build directory, and a web directory"); - } -} - -if ($nickname eq "") { - die ("Please invoke NewNightlyTest.pl with command line option " . - "\"-nickname <nickname>\""); -} - -my $LLVMSrcDir = $ENV{'LLVMSRCDIR'}; -$LLVMSrcDir = "$BuildDir/llvm" unless $LLVMSrcDir; -my $LLVMObjDir = $ENV{'LLVMOBJDIR'}; -$LLVMObjDir = "$BuildDir/llvm" unless $LLVMObjDir; -my $LLVMTestDir = $ENV{'LLVMTESTDIR'}; -$LLVMTestDir = "$BuildDir/llvm/projects/llvm-test" unless $LLVMTestDir; - -############################################################## -# -# Define the file names we'll use -# -############################################################## - -my $Prefix = "$WebDir/$DATE"; -my $SingleSourceLog = "$Prefix-SingleSource-ProgramTest.txt.gz"; -my $MultiSourceLog = "$Prefix-MultiSource-ProgramTest.txt.gz"; -my $ExternalLog = "$Prefix-External-ProgramTest.txt.gz"; - -# These are only valid in non-config mode. -my $ConfigureLog = "", $BuildLog = "", $COLog = ""; -my $DejagnuLog = "", $DejagnuSum = "", $DejagnuLog = ""; - -# Are we in config mode? -my $ConfigMode = 0; - -############################################################## -# -# Helper functions -# -############################################################## - -sub GetDir { - my $Suffix = shift; - opendir DH, $WebDir; - my @Result = reverse sort grep !/$DATE/, grep /[-0-9]+$Suffix/, readdir DH; - closedir DH; - return @Result; -} - -sub RunLoggedCommand { - my $Command = shift; - my $Log = shift; - my $Title = shift; - if ($TEELOGS) { - if ($VERBOSE) { - print "$Title\n"; - print "$Command 2>&1 | tee $Log\n"; - } - system "$Command 2>&1 | tee $Log"; - } else { - if ($VERBOSE) { - print "$Title\n"; - print "$Command > $Log 2>&1\n"; - } - system "$Command > $Log 2>&1"; - } -} - -sub RunAppendingLoggedCommand { - my $Command = shift; - my $Log = shift; - my $Title = shift; - if ($TEELOGS) { - if ($VERBOSE) { - print "$Title\n"; - print "$Command 2>&1 | tee -a $Log\n"; - } - system "$Command 2>&1 | tee -a $Log"; - } else { - if ($VERBOSE) { - print "$Title\n"; - print "$Command >> $Log 2>&1\n"; - } - system "$Command >> $Log 2>&1"; - } -} - -sub GetRegex { # (Regex with ()'s, value) - if ($_[1] =~ /$_[0]/m) { - return $1; - } - return "0"; -} - -sub ChangeDir { # directory, logical name - my ($dir,$name) = @_; - chomp($dir); - if ( $VERBOSE ) { print "Changing To: $name ($dir)\n"; } - $result = chdir($dir); - if (!$result) { - print "ERROR!!! Cannot change directory to: $name ($dir) because $!\n"; - return false; - } - return true; -} - -sub ReadFile { - if (open (FILE, $_[0])) { - undef $/; - my $Ret = <FILE>; - close FILE; - $/ = '\n'; - return $Ret; - } else { - print "Could not open file '$_[0]' for reading!\n"; - return ""; - } -} - -sub WriteFile { # (filename, contents) - open (FILE, ">$_[0]") or die "Could not open file '$_[0]' for writing!\n"; - print FILE $_[1]; - close FILE; -} - -sub CopyFile { #filename, newfile - my ($file, $newfile) = @_; - chomp($file); - if ($VERBOSE) { print "Copying $file to $newfile\n"; } - copy($file, $newfile); -} - -#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# This function acts as a mini web browswer submitting data -# to our central server via the post method -# -#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -sub WriteSentData { - $variables = $_[0]; - - # Write out the "...-sentdata.txt" file. - - my $sentdata=""; - foreach $x (keys (%$variables)){ - $value = $variables->{$x}; - $sentdata.= "$x => $value\n"; - } - WriteFile "$Prefix-sentdata.txt", $sentdata; -} - -sub SendData { - $host = $_[0]; - $file = $_[1]; - $variables = $_[2]; - - if (!($SUBMITAUX eq "")) { - system "$SUBMITAUX \"$Prefix-sentdata.txt\""; - } - - if (!$SUBMIT) { - return "Skipped standard submit.\n"; - } - - # Create the content to send to the server. - - my $content; - foreach $key (keys (%$variables)){ - $value = $variables->{$key}; - $value =~ s/([^A-Za-z0-9])/sprintf("%%%02X", ord($1))/seg; - $content .= "$key=$value&"; - } - - # Send the data to the server. - # - # FIXME: This code should be more robust? - - $port=80; - $socketaddr= sockaddr_in $port, inet_aton $host or die "Bad hostname\n"; - socket SOCK, PF_INET, SOCK_STREAM, getprotobyname('tcp') or - die "Bad socket\n"; - connect SOCK, $socketaddr or die "Bad connection\n"; - select((select(SOCK), $| = 1)[0]); - - $length = length($content); - - my $send= "POST $file HTTP/1.0\n"; - $send.= "Host: $host\n"; - $send.= "Content-Type: application/x-www-form-urlencoded\n"; - $send.= "Content-length: $length\n\n"; - $send.= "$content"; - - print SOCK $send; - my $result; - while(<SOCK>){ - $result .= $_; - } - close(SOCK); - - return $result; -} - -############################################################## -# -# Individual Build & Test Functions -# -############################################################## - -# Create the source repository directory. -sub CheckoutSource { - die "Invalid call!" unless $ConfigMode == 0; - if (-d $BuildDir) { - if (!$NOREMOVE) { - if ( $VERBOSE ) { - print "Build directory exists! Removing it\n"; - } - system "rm -rf $BuildDir"; - mkdir $BuildDir or die "Could not create checkout directory $BuildDir!"; - } else { - if ( $VERBOSE ) { - print "Build directory exists!\n"; - } - } - } else { - mkdir $BuildDir or die "Could not create checkout directory $BuildDir!"; - } - - ChangeDir( $BuildDir, "checkout directory" ); - my $SVNCMD = "$NICE svn co --non-interactive"; - RunLoggedCommand("( time -p $SVNCMD $SVNURL/llvm/trunk llvm; cd llvm/projects ; " . - " $SVNCMD $TestSVNURL/test-suite/trunk llvm-test )", $COLog, - "CHECKOUT LLVM"); - if ($WITHCLANG) { - RunLoggedCommand("( cd llvm/tools ; " . - " $SVNCMD $SVNURL/cfe/trunk clang )", $COLog, - "CHECKOUT CLANG"); - } -} - -# Build the entire tree, saving build messages to the build log. Returns false -# on build failure. -sub BuildLLVM { - die "Invalid call!" unless $ConfigMode == 0; - my $EXTRAFLAGS = "--enable-spec --with-objroot=."; - RunLoggedCommand("(time -p $NICE ./configure $CONFIGUREARGS $EXTRAFLAGS) ", - $ConfigureLog, "CONFIGURE"); - # Build the entire tree, capturing the output into $BuildLog - if (!$NOCLEAN) { - RunAppendingLoggedCommand("($NICE $MAKECMD $MAKEOPTS clean)", $BuildLog, "BUILD CLEAN"); - } - RunAppendingLoggedCommand("(time -p $NICE $MAKECMD $MAKEOPTS)", $BuildLog, "BUILD"); - - if (`grep -a '^$MAKECMD\[^:]*: .*Error' $BuildLog | wc -l` + 0 || - `grep -a '^$MAKECMD: \*\*\*.*Stop.' $BuildLog | wc -l` + 0) { - return 0; - } - - return 1; -} - -# Run the named tests (i.e. "SingleSource" "MultiSource" "External") -sub TestDirectory { - my $SubDir = shift; - ChangeDir( "$LLVMTestDir/$SubDir", - "Programs Test Subdirectory" ) || return ("", ""); - - my $ProgramTestLog = "$Prefix-$SubDir-ProgramTest.txt"; - - # Make sure to clean the test results. - RunLoggedCommand("$MAKECMD -k $MAKEOPTS $PROGTESTOPTS clean $TESTFLAGS", - $ProgramTestLog, "TEST DIRECTORY $SubDir"); - - # Run the programs tests... creating a report.nightly.csv file. - my $LLCBetaOpts = ""; - RunLoggedCommand("$MAKECMD -k $MAKEOPTS $PROGTESTOPTS report.nightly.csv ". - "$TESTFLAGS TEST=nightly", - $ProgramTestLog, "TEST DIRECTORY $SubDir"); - $LLCBetaOpts = `$MAKECMD print-llcbeta-option`; - - my $ProgramsTable; - if (`grep -a '^$MAKECMD\[^:]: .*Error' $ProgramTestLog | wc -l` + 0) { - $ProgramsTable="Error running test $SubDir\n"; - print "ERROR TESTING\n"; - } elsif (`grep -a '^$MAKECMD\[^:]: .*No rule to make target' $ProgramTestLog | wc -l` + 0) { - $ProgramsTable="Makefile error running tests $SubDir!\n"; - print "ERROR TESTING\n"; - } else { - # Create a list of the tests which were run... - system "egrep -a 'TEST-(PASS|FAIL)' < $ProgramTestLog ". - "| sort > $Prefix-$SubDir-Tests.txt"; - } - $ProgramsTable = ReadFile "report.nightly.csv"; - - ChangeDir( "../../..", "Programs Test Parent Directory" ); - return ($ProgramsTable, $LLCBetaOpts); -} - -# Run all the nightly tests and return the program tables and the list of tests, -# passes, fails, and xfails. -sub RunNightlyTest() { - ($SSProgs, $llcbeta_options) = TestDirectory("SingleSource"); - WriteFile "$Prefix-SingleSource-Performance.txt", $SSProgs; - ($MSProgs, $llcbeta_options) = TestDirectory("MultiSource"); - WriteFile "$Prefix-MultiSource-Performance.txt", $MSProgs; - if ( ! $NOEXTERNALS ) { - ($ExtProgs, $llcbeta_options) = TestDirectory("External"); - WriteFile "$Prefix-External-Performance.txt", $ExtProgs; - system "cat $Prefix-SingleSource-Tests.txt " . - "$Prefix-MultiSource-Tests.txt ". - "$Prefix-External-Tests.txt | sort > $Prefix-Tests.txt"; - system "cat $Prefix-SingleSource-Performance.txt " . - "$Prefix-MultiSource-Performance.txt ". - "$Prefix-External-Performance.txt | sort > $Prefix-Performance.txt"; - } else { - $ExtProgs = "External TEST STAGE SKIPPED\n"; - if ( $VERBOSE ) { - print "External TEST STAGE SKIPPED\n"; - } - system "cat $Prefix-SingleSource-Tests.txt " . - "$Prefix-MultiSource-Tests.txt ". - " | sort > $Prefix-Tests.txt"; - system "cat $Prefix-SingleSource-Performance.txt " . - "$Prefix-MultiSource-Performance.txt ". - " | sort > $Prefix-Performance.txt"; - } - - # Compile passes, fails, xfails. - my $All = (ReadFile "$Prefix-Tests.txt"); - my @TestSuiteResultLines = split "\n", $All; - my ($Passes, $Fails, $XFails) = ""; - - for ($x=0; $x < @TestSuiteResultLines; $x++) { - if (@TestSuiteResultLines[$x] =~ m/^PASS:/) { - $Passes .= "$TestSuiteResultLines[$x]\n"; - } - elsif (@TestSuiteResultLines[$x] =~ m/^FAIL:/) { - $Fails .= "$TestSuiteResultLines[$x]\n"; - } - elsif (@TestSuiteResultLines[$x] =~ m/^XFAIL:/) { - $XFails .= "$TestSuiteResultLines[$x]\n"; - } - } - - return ($SSProgs, $MSProgs, $ExtProgs, $All, $Passes, $Fails, $XFails); -} - -############################################################## -# -# Initialize filenames -# -############################################################## - -if (! -d $WebDir) { - mkdir $WebDir, 0777 or die "Unable to create web directory: '$WebDir'."; - if($VERBOSE){ - warn "$WebDir did not exist; creating it.\n"; - } -} - -if ($CONFIG_PATH ne "") { - $ConfigMode = 1; - $LLVMSrcDir = GetRegex "^(.*)\\s+", `$CONFIG_PATH --src-root`; - $LLVMObjDir = GetRegex "^(.*)\\s+", `$CONFIG_PATH --obj-root`; - # FIXME: Add llvm-config hook for this? - $LLVMTestDir = $LLVMObjDir . "/projects/test-suite"; -} else { - $ConfigureLog = "$Prefix-Configure-Log.txt"; - $BuildLog = "$Prefix-Build-Log.txt"; - $COLog = "$Prefix-CVS-Log.txt"; -} - -if ($VERBOSE) { - if ($CONFIG_PATH ne "") { - print "INITIALIZED (config mode)\n"; - print "WebDir = $WebDir\n"; - print "Prefix = $Prefix\n"; - print "LLVM Src = $LLVMSrcDir\n"; - print "LLVM Obj = $LLVMObjDir\n"; - print "LLVM Test = $LLVMTestDir\n"; - } else { - print "INITIALIZED\n"; - print "SVN URL = $SVNURL\n"; - print "COLog = $COLog\n"; - print "BuildDir = $BuildDir\n"; - print "WebDir = $WebDir\n"; - print "Prefix = $Prefix\n"; - print "BuildLog = $BuildLog\n"; - } -} - -############################################################## -# -# The actual NewNightlyTest logic. -# -############################################################## - -$starttime = `date "+20%y-%m-%d %H:%M:%S"`; - -my $BuildError = 0, $BuildStatus = "OK"; -if ($ConfigMode == 0) { - if (!$NOCHECKOUT) { - CheckoutSource(); - } - - # Build LLVM. - ChangeDir( $LLVMSrcDir , "llvm source directory") ; - if ($NOCHECKOUT || $NOBUILD) { - $BuildStatus = "Skipped by user"; - } else { - if (!BuildLLVM()) { - if( $VERBOSE) { print "\n***ERROR BUILDING TREE\n\n"; } - $BuildError = 1; - $BuildStatus = "Error: compilation aborted"; - } - } -} - -# Run the llvm-test tests. -my ($SingleSourceProgramsTable, $MultiSourceProgramsTable, $ExternalProgramsTable, - $all_tests, $passes, $fails, $xfails) = ""; -if (!$NOTEST && !$BuildError) { - ($SingleSourceProgramsTable, $MultiSourceProgramsTable, $ExternalProgramsTable, - $all_tests, $passes, $fails, $xfails) = RunNightlyTest(); -} - -$endtime = `date "+20%y-%m-%d %H:%M:%S"`; - -# The last bit of logic is to remove the build and web dirs, after sending data -# to the server. - -############################################################## -# -# Accumulate the information to send to the server. -# -############################################################## - -if ( $VERBOSE ) { print "PREPARING LOGS TO BE SENT TO SERVER\n"; } - -if ( ! $NOUNAME ) { - $machine_data = "uname: ".`uname -a`. - "hardware: ".`uname -m`. - "os: ".`uname -sr`. - "name: ".`uname -n`. - "date: ".`date \"+20%y-%m-%d\"`. - "time: ".`date +\"%H:%M:%S\"`; -} else { - $machine_data = "uname: (excluded)\n". - "hardware: ".`uname -m`. - "os: ".`uname -sr`. - "name: $nickname\n". - "date: ".`date \"+20%y-%m-%d\"`. - "time: ".`date +\"%H:%M:%S\"`; -} - -# Get gcc version. -my $gcc_version_long = ""; -if ($GCCPATH ne "") { - $gcc_version_long = `$GCCPATH/gcc --version`; -} elsif ($ENV{"CC"}) { - $gcc_version_long = `$ENV{"CC"} --version`; -} else { - $gcc_version_long = `gcc --version`; -} -my $gcc_version = (split '\n', $gcc_version_long)[0]; - -# Get llvm-gcc target triple. -# -# FIXME: This shouldn't be hardwired to llvm-gcc. -my $llvmgcc_version_long = ""; -if ($LLVMGCCPATH ne "") { - $llvmgcc_version_long = `$LLVMGCCPATH/llvm-gcc -v 2>&1`; -} else { - $llvmgcc_version_long = `llvm-gcc -v 2>&1`; -} -(split '\n', $llvmgcc_version_long)[1] =~ /Target: (.+)/; -my $targetTriple = $1; - -# Logs. -my ($ConfigureLogData, $BuildLogData, $CheckoutLogData) = ""; -if ($ConfigMode == 0) { - $ConfigureLogData = ReadFile $ConfigureLog; - $BuildLogData = ReadFile $BuildLog; - $CheckoutLogData = ReadFile $COLog; -} - -# Checkout info. -my $CheckoutTime_Wall = GetRegex "^real ([0-9.]+)", $CheckoutLogData; -my $CheckoutTime_User = GetRegex "^user ([0-9.]+)", $CheckoutLogData; -my $CheckoutTime_Sys = GetRegex "^sys ([0-9.]+)", $CheckoutLogData; -my $CheckoutTime_CPU = $CVSCheckoutTime_User + $CVSCheckoutTime_Sys; - -# Configure info. -my $ConfigTimeU = GetRegex "^user ([0-9.]+)", $ConfigureLogData; -my $ConfigTimeS = GetRegex "^sys ([0-9.]+)", $ConfigureLogData; -my $ConfigTime = $ConfigTimeU+$ConfigTimeS; # ConfigTime = User+System -my $ConfigWallTime = GetRegex "^real ([0-9.]+)",$ConfigureLogData; -$ConfigTime=-1 unless $ConfigTime; -$ConfigWallTime=-1 unless $ConfigWallTime; - -# Build info. -my $BuildTimeU = GetRegex "^user ([0-9.]+)", $BuildLogData; -my $BuildTimeS = GetRegex "^sys ([0-9.]+)", $BuildLogData; -my $BuildTime = $BuildTimeU+$BuildTimeS; # BuildTime = User+System -my $BuildWallTime = GetRegex "^real ([0-9.]+)", $BuildLogData; -$BuildTime=-1 unless $BuildTime; -$BuildWallTime=-1 unless $BuildWallTime; - -if ( $VERBOSE ) { print "SEND THE DATA VIA THE POST REQUEST\n"; } - -my %hash_of_data = ( - 'machine_data' => $machine_data, - 'build_data' => $ConfigureLogData . $BuildLogData, - 'gcc_version' => $gcc_version, - 'nickname' => $nickname, - 'dejagnutime_wall' => "0.0", - 'dejagnutime_cpu' => "0.0", - 'cvscheckouttime_wall' => $CheckoutTime_Wall, - 'cvscheckouttime_cpu' => $CheckoutTime_CPU, - 'configtime_wall' => $ConfigWallTime, - 'configtime_cpu'=> $ConfigTime, - 'buildtime_wall' => $BuildWallTime, - 'buildtime_cpu' => $BuildTime, - 'buildstatus' => $BuildStatus, - 'singlesource_programstable' => $SingleSourceProgramsTable, - 'multisource_programstable' => $MultiSourceProgramsTable, - 'externalsource_programstable' => $ExternalProgramsTable, - 'llcbeta_options' => $llcbeta_options, - 'passing_tests' => $passes, - 'expfail_tests' => $xfails, - 'unexpfail_tests' => $fails, - 'all_tests' => $all_tests, - 'dejagnutests_results' => "Dejagnu skipped by user choice.", - 'dejagnutests_log' => "", - 'starttime' => $starttime, - 'endtime' => $endtime, - 'target_triple' => $targetTriple, - - # Unused, but left around for backwards compatibility. - 'warnings' => "", - 'cvsusercommitlist' => "", - 'cvsuserupdatelist' => "", - 'cvsaddedfiles' => "", - 'cvsmodifiedfiles' => "", - 'cvsremovedfiles' => "", - 'lines_of_code' => "", - 'cvs_file_count' => 0, - 'cvs_dir_count' => 0, - 'warnings_removed' => "", - 'warnings_added' => "", - 'new_tests' => "", - 'removed_tests' => "", - 'o_file_sizes' => "", - 'a_file_sizes' => "" -); - -# Write out the "...-sentdata.txt" file. -WriteSentData \%hash_of_data; - -if ($SUBMIT || !($SUBMITAUX eq "")) { - my $response = SendData $SUBMITSERVER,$SUBMITSCRIPT,\%hash_of_data; - if( $VERBOSE) { print "============================\n$response"; } -} else { - print "============================\n"; - foreach $x(keys %hash_of_data){ - print "$x => $hash_of_data{$x}\n"; - } -} - -############################################################## -# -# Remove the source tree... -# -############################################################## -system ( "$NICE rm -rf $BuildDir") - if (!$NOCHECKOUT and !$NOREMOVE and !$NOREMOVEATEND); -system ( "$NICE rm -rf $WebDir") - if (!$NOCHECKOUT and !$NOREMOVE and !$NOREMOVERESULTS); diff --git a/utils/NightlyTest.gnuplot b/utils/NightlyTest.gnuplot deleted file mode 100644 index 514b72a..0000000 --- a/utils/NightlyTest.gnuplot +++ /dev/null @@ -1,214 +0,0 @@ -set terminal png - -##------- Plot small Date vs LOC ---- -set output "running_loc.png" -set xlabel "Date" -set ylabel "Lines of Code" -set xdata time -set timefmt "%Y-%m-%d-%H:%M:%S:" -set format x "%b %d, %Y" - -set size .75,.75 -set xtics rotate -set xlabel 0,-1 -plot "running_loc.txt" using 1:2 title '' with lines, \ - "running_loc.txt" using 1:2 title "Date vs. Lines of Code" with lines - -##------- Plot large Date vs LOC ---- -set size 1.5,1.5 -set xtics norotate -set xlabel 0,0 -set output "running_loc_large.png" -plot "running_loc.txt" using 1:2 title '', \ - "running_loc.txt" using 1:2 title "Date vs. Lines of Code" with lines - - -# Delete all labels... -set nolabel - -##------- Olden CBE performance ---- - -set size .75,.75 -set xtics rotate -set xlabel 0,-1 -set output "running_Olden_cbe_time.png" -set ylabel "CBE compiled execution time (s)" -plot "running_Olden_cbe_time.txt" u 1:2 t '' with lines, \ - "running_Olden_cbe_time.txt" u 1:2 t "bh" with lines, \ - "running_Olden_cbe_time.txt" u 1:3 t "em3d" with lines, \ - "running_Olden_cbe_time.txt" u 1:4 t "mst" with lines, \ - "running_Olden_cbe_time.txt" u 1:5 t "power" with lines, \ - "running_Olden_cbe_time.txt" u 1:6 t "tsp" with lines, \ - "running_Olden_cbe_time.txt" u 1:7 t "bisort" with lines, \ - "running_Olden_cbe_time.txt" u 1:8 t "health" with lines, \ - "running_Olden_cbe_time.txt" u 1:9 t "perimeter" with lines, \ - "running_Olden_cbe_time.txt" u 1:10 t "treeadd" with lines, \ - "running_Olden_cbe_time.txt" u 1:11 t "voronoi" \ - with lines - -set size 1.5,1.5 -set xtics norotate -set xlabel 0,0 -set output "running_Olden_cbe_time_large.png" -plot "running_Olden_cbe_time.txt" u 1:2 t '' with lines, \ - "running_Olden_cbe_time.txt" u 1:2 t "bh" with lines, \ - "running_Olden_cbe_time.txt" u 1:3 t "em3d" with lines, \ - "running_Olden_cbe_time.txt" u 1:4 t "mst" with lines, \ - "running_Olden_cbe_time.txt" u 1:5 t "power" with lines, \ - "running_Olden_cbe_time.txt" u 1:6 t "tsp" with lines, \ - "running_Olden_cbe_time.txt" u 1:7 t "bisort" with lines, \ - "running_Olden_cbe_time.txt" u 1:8 t "health" with lines, \ - "running_Olden_cbe_time.txt" u 1:9 t "perimeter" with lines, \ - "running_Olden_cbe_time.txt" u 1:10 t "treeadd" with lines, \ - "running_Olden_cbe_time.txt" u 1:11 t "voronoi" \ - with lines - -##------- Olden JIT performance ---- - -set size .75,.75 -set xtics rotate -set xlabel 0,-1 -set output "running_Olden_jit_time.png" -set ylabel "JIT execution time (s)" -plot "running_Olden_jit_time.txt" u 1:2 t '' with lines, \ - "running_Olden_jit_time.txt" u 1:2 t "bh" with lines, \ - "running_Olden_jit_time.txt" u 1:3 t "em3d" with lines, \ - "running_Olden_jit_time.txt" u 1:4 t "mst" with lines, \ - "running_Olden_jit_time.txt" u 1:5 t "power" with lines, \ - "running_Olden_jit_time.txt" u 1:6 t "tsp" with lines, \ - "running_Olden_jit_time.txt" u 1:7 t "bisort" with lines, \ - "running_Olden_jit_time.txt" u 1:8 t "health" with lines, \ - "running_Olden_jit_time.txt" u 1:9 t "perimeter" with lines, \ - "running_Olden_jit_time.txt" u 1:10 t "treeadd" with lines, \ - "running_Olden_jit_time.txt" u 1:11 t "voronoi" \ - with lines - -set size 1.5,1.5 -set xtics norotate -set xlabel 0,0 -set output "running_Olden_jit_time_large.png" -plot "running_Olden_jit_time.txt" u 1:2 t '' with lines, \ - "running_Olden_jit_time.txt" u 1:2 t "bh" with lines, \ - "running_Olden_jit_time.txt" u 1:3 t "em3d" with lines, \ - "running_Olden_jit_time.txt" u 1:4 t "mst" with lines, \ - "running_Olden_jit_time.txt" u 1:5 t "power" with lines, \ - "running_Olden_jit_time.txt" u 1:6 t "tsp" with lines, \ - "running_Olden_jit_time.txt" u 1:7 t "bisort" with lines, \ - "running_Olden_jit_time.txt" u 1:8 t "health" with lines, \ - "running_Olden_jit_time.txt" u 1:9 t "perimeter" with lines, \ - "running_Olden_jit_time.txt" u 1:10 t "treeadd" with lines, \ - "running_Olden_jit_time.txt" u 1:11 t "voronoi" \ - with lines - -##------- Olden LLC performance ---- - -set size .75,.75 -set xtics rotate -set xlabel 0,-1 -set output "running_Olden_llc_time.png" -set ylabel "LLC compiled execution time (s)" -plot "running_Olden_llc_time.txt" u 1:2 t '' with lines, \ - "running_Olden_llc_time.txt" u 1:2 t "bh" with lines, \ - "running_Olden_llc_time.txt" u 1:3 t "em3d" with lines, \ - "running_Olden_llc_time.txt" u 1:4 t "mst" with lines, \ - "running_Olden_llc_time.txt" u 1:5 t "power" with lines, \ - "running_Olden_llc_time.txt" u 1:6 t "tsp" with lines, \ - "running_Olden_llc_time.txt" u 1:7 t "bisort" with lines, \ - "running_Olden_llc_time.txt" u 1:8 t "health" with lines, \ - "running_Olden_llc_time.txt" u 1:9 t "perimeter" with lines, \ - "running_Olden_llc_time.txt" u 1:10 t "treeadd" with lines, \ - "running_Olden_llc_time.txt" u 1:11 t "voronoi" \ - with lines - -set size 1.5,1.5 -set xtics norotate -set xlabel 0,0 -set output "running_Olden_llc_time_large.png" -plot "running_Olden_llc_time.txt" u 1:2 t '' with lines, \ - "running_Olden_llc_time.txt" u 1:2 t "bh" with lines, \ - "running_Olden_llc_time.txt" u 1:3 t "em3d" with lines, \ - "running_Olden_llc_time.txt" u 1:4 t "mst" with lines, \ - "running_Olden_llc_time.txt" u 1:5 t "power" with lines, \ - "running_Olden_llc_time.txt" u 1:6 t "tsp" with lines, \ - "running_Olden_llc_time.txt" u 1:7 t "bisort" with lines, \ - "running_Olden_llc_time.txt" u 1:8 t "health" with lines, \ - "running_Olden_llc_time.txt" u 1:9 t "perimeter" with lines, \ - "running_Olden_llc_time.txt" u 1:10 t "treeadd" with lines, \ - "running_Olden_llc_time.txt" u 1:11 t "voronoi" \ - with lines - - -##------- Olden optimizer time ---- - -set size .75,.75 -set xtics rotate -set xlabel 0,-1 -set output "running_Olden_opt_time.png" -set ylabel "Time to run the optimizer (s)" -plot "running_Olden_opt_time.txt" u 1:2 t '' with lines, \ - "running_Olden_opt_time.txt" u 1:2 t "bh" with lines, \ - "running_Olden_opt_time.txt" u 1:3 t "em3d" with lines, \ - "running_Olden_opt_time.txt" u 1:4 t "mst" with lines, \ - "running_Olden_opt_time.txt" u 1:5 t "power" with lines, \ - "running_Olden_opt_time.txt" u 1:6 t "tsp" with lines, \ - "running_Olden_opt_time.txt" u 1:7 t "bisort" with lines, \ - "running_Olden_opt_time.txt" u 1:8 t "health" with lines, \ - "running_Olden_opt_time.txt" u 1:9 t "perimeter" with lines, \ - "running_Olden_opt_time.txt" u 1:10 t "treeadd" with lines, \ - "running_Olden_opt_time.txt" u 1:11 t "voronoi" \ - with lines - -set size 1.5,1.5 -set xtics norotate -set xlabel 0,0 -set output "running_Olden_opt_time_large.png" -plot "running_Olden_opt_time.txt" u 1:2 t '' with lines, \ - "running_Olden_opt_time.txt" u 1:2 t "bh" with lines, \ - "running_Olden_opt_time.txt" u 1:3 t "em3d" with lines, \ - "running_Olden_opt_time.txt" u 1:4 t "mst" with lines, \ - "running_Olden_opt_time.txt" u 1:5 t "power" with lines, \ - "running_Olden_opt_time.txt" u 1:6 t "tsp" with lines, \ - "running_Olden_opt_time.txt" u 1:7 t "bisort" with lines, \ - "running_Olden_opt_time.txt" u 1:8 t "health" with lines, \ - "running_Olden_opt_time.txt" u 1:9 t "perimeter" with lines, \ - "running_Olden_opt_time.txt" u 1:10 t "treeadd" with lines, \ - "running_Olden_opt_time.txt" u 1:11 t "voronoi" \ - with lines - - -##------- Bytecode size ---- - -set size .75,.75 -set xtics rotate -set xlabel 0,-1 -set output "running_Olden_bytecode.png" -set ylabel "Program bytecode size (bytes)" -plot "running_Olden_bytecode.txt" u 1:2 t '' with lines, \ - "running_Olden_bytecode.txt" u 1:2 t "bh" with lines, \ - "running_Olden_bytecode.txt" u 1:3 t "em3d" with lines, \ - "running_Olden_bytecode.txt" u 1:4 t "mst" with lines, \ - "running_Olden_bytecode.txt" u 1:5 t "power" with lines, \ - "running_Olden_bytecode.txt" u 1:6 t "tsp" with lines, \ - "running_Olden_bytecode.txt" u 1:7 t "bisort" with lines, \ - "running_Olden_bytecode.txt" u 1:8 t "health" with lines, \ - "running_Olden_bytecode.txt" u 1:9 t "perimeter" with lines, \ - "running_Olden_bytecode.txt" u 1:10 t "treeadd" with lines, \ - "running_Olden_bytecode.txt" u 1:11 t "voronoi" \ - with lines - -set size 1.5,1.5 -set xtics norotate -set xlabel 0,0 -set output "running_Olden_bytecode_large.png" -plot "running_Olden_bytecode.txt" u 1:2 t '' with lines, \ - "running_Olden_bytecode.txt" u 1:2 t "bh" with lines, \ - "running_Olden_bytecode.txt" u 1:3 t "em3d" with lines, \ - "running_Olden_bytecode.txt" u 1:4 t "mst" with lines, \ - "running_Olden_bytecode.txt" u 1:5 t "power" with lines, \ - "running_Olden_bytecode.txt" u 1:6 t "tsp" with lines, \ - "running_Olden_bytecode.txt" u 1:7 t "bisort" with lines, \ - "running_Olden_bytecode.txt" u 1:8 t "health" with lines, \ - "running_Olden_bytecode.txt" u 1:9 t "perimeter" with lines, \ - "running_Olden_bytecode.txt" u 1:10 t "treeadd" with lines, \ - "running_Olden_bytecode.txt" u 1:11 t "voronoi" \ - with lines diff --git a/utils/NightlyTestTemplate.html b/utils/NightlyTestTemplate.html deleted file mode 100644 index c38bb2e..0000000 --- a/utils/NightlyTestTemplate.html +++ /dev/null @@ -1,244 +0,0 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> -<html><head><title>LLVM Test Results for $DateString</title></head> - -<body bgcolor=white> -<center><font size=+3 face=Verdana><b>LLVM Test Results for $DateString</b></font></center> -<hr height=1> - -<table width=100%> -<tr><td valign=top align=center> - -<table border="0" cellspacing="0" cellpadding="2"><tr><td bgcolor="#000000"> -<table border="0" cellpadding="5" cellspacing="0"><tr><td bgcolor="#DDAA77"> -<font size=+1><b>Sections:</b></font><br> -</td></tr><tr><td bgcolor="#FFCC99" align=center> -<a href="#Overview">Overview</a><br> -<a href="#Changes">Changes</a><br> -<a href="#Dejagnu">Dejagnu Tests</a><br> -<a href="#Trends">Trends</a><br> -<a href="#Programs">Programs</a><br> -</td></tr></table></td></tr></table> - -<p> -<table border="0" cellspacing="0" cellpadding="2"><tr><td bgcolor="#000000"> -<table border="0" cellpadding="5" cellspacing="0"><tr><td bgcolor="#DDAA77" -<font size=+1><b>Previous:</b></font><br> -</td></tr><tr><td bgcolor="#FFCC99"> - $PrevDaysList -</td></tr></table></td></tr></table> -<p> - -<font size=+1><b>Back to:</b></font><br> -<a href="http://llvm.org/testresults/">Test Results</a><br> -<a href="http://llvm.org/">LLVM Page</a><p> - -</td><td valign=top> - -<center> -<table border="0" cellspacing="0" cellpadding="2"><tr><td bgcolor="#000000"> -<table border="0" cellpadding="10" cellspacing="0"><tr><td bgcolor="#DDAA77" -<font size=+2 face=Verdana><b><a name="Overview">Today's Test Results Overview</font></b> -</td></tr></table></td></tr></table></center><p> - -<!-- Running LOC graph --> -<table align=right> -<tr><td> -<a href="running_loc_large.png" - ><img border=0 width=480 height=360 src="running_loc.png"></a> -</td></tr> -<tr><td align=center>Lines Of Code over Time<br> -<font size=-1><a href="running_loc_large.png">Click for larger view</a></font> -</td></tr> -</table> - -<h2>Nightly Test Overview:</h2> -<ul> - <li>Start: <b>$TestStartTime</b></li> - <li>Finish: <b>$TestFinishTime</b></li> - <li>Platform: <b>$TestPlatform</b></li> -</ul> -<h2>CVS Tree Overview:</h2> -<ul> -<li><a href="$DATE-CVS-Log.txt">CVS Checkout Log</a> -<ul> - <b>$NumDirsInCVS</b> dirs, <b>$NumFilesInCVS</b> files, <b>$LOC</b> - lines of code, checked out in <b>$CVSCheckoutTime</b> seconds<br></ul> -<li><a href="$DATE-Build-Log.txt">Compilation Log</a> -<table> -<tr><td><b>Item</b></td><td><b>CPU Time</b></td><td><b>Wall Clock</b></td></tr> -<tr><td>Configure CVS Tree</td><td>$ConfigTime</td><td>$ConfigWallTime</td></tr> -<tr><td>Build CVS Tree</td><td>$BuildTime</td><td>$BuildWallTime</td></tr> -<tr><td>Run Dejagnu Tests</td><td>$DejagnuTime</td><td>$DejagnuWallTime</td></tr> -</table></li> -<li>Number of object files compiled: <b>$NumObjects</b></li> -<li>Number of libraries linked: <b>$NumLibraries</b></li> -<li>Number of executables linked:<b> $NumExecutables</b></li> -<li>Build Status: $BuildStatus</li> -</ul> - -<h2>Warnings during the build:</h2> -$WarningsList - -<br><br><center> -<table border="0" cellspacing="0" cellpadding="2"><tr><td bgcolor="#000000"> -<table border="0" cellpadding="10" cellspacing="0"><tr><td bgcolor="#DDAA77" -<font size=+2 face=Verdana><b><a name="Changes">Changes from Yesterday</font></b> -</td></tr></table></td></tr></table></center><p> - -<h2>Changes to CVS:</h2> -<ul> -<li>Users who committed to CVS: <b>$UserCommitList</b> -<li>Users who updated from CVS: <b>$UserUpdateList</b> -<li>Added Files: $AddedFilesList -<li>Modified Files: $ModifiedFilesList -<li>Removed Files: $RemovedFilesList -</ul><p> - -<h2>Changes to Warnings:</h2> -<p>Warnings Added:</p> -$WarningsAdded -<p>Warnings Removed:</p> -$WarningsRemoved - -<h2>Changes in the test suite:</h2> -<ul> -<li>New Tests: $TestsAdded -<li>Removed Tests: $TestsRemoved -<li>Newly passing tests: $TestsFixed -<li>Newly failing tests: $TestsBroken -</ul> -</td></tr></tbody></table> - - -<br/><br/><center> -<table border="0" cellspacing="0" cellpadding="2"><tr><td bgcolor="#000000"> -<table border="0" cellpadding="10" cellspacing="0"><tr><td bgcolor="#DDAA77" -<font size=+2 face=Verdana><b><a name="Dejagnu">Dejagnu Test Results</font></b> -</td></tr></table></td></tr></table></center> -<br/> -$DejagnuTestResults -<p>A complete log of testing <a href="$DATE-Dejagnu-testrun.log">Feature and Regression</a> is available for further analysis.</p> - -<br><br><center> -<table border="0" cellspacing="0" cellpadding="2"><tr><td bgcolor="#000000"> -<table border="0" cellpadding="10" cellspacing="0"><tr><td bgcolor="#DDAA77" -<font size=+2 face=Verdana><b><a name="Trends">Changes Over Time</font></b> -</td></tr></table></td></tr></table></center><p> - - -Here are some charts showing how the LLVM optimizer and code generators are -changing over time. For now we use the Olden benchmark suite to measure this, -but eventually we will switch to using SPEC CPU2000. All programs are run with -"LARGE_PROBLEM_SIZE" enabled. Click on any of the charts to get a larger -version.<p> - -<h2>Compilation Measurements:</h2> - -<table border="0" align=center> -<tr> -<td width=50% align=center> -<a href="running_Olden_bytecode_large.png"><img width=480 height=360 border=0 src="running_Olden_bytecode.png"></a><br> -Size of LLVM bytecode files -</td> -<td width=50% align=center> -<a href="running_Olden_opt_time_large.png"><img width=480 height=360 border=0 src="running_Olden_opt_time.png"></a><br> -Time to run the LLVM optimizer on each program -</td></tr> -</table> - -<h2>Program Execution Measurements:</h2> - -<table border="0" align=center> -<tr> -<td width=50% align=center> -<a href="running_Olden_cbe_time_large.png"><img width=480 height=360 border=0 src="running_Olden_cbe_time.png"></a><br> -Execution time for CBE generated executable -</td> -<td width=50% align=center> -<a href="running_Olden_llc_time_large.png"><img width=480 height=360 border=0 src="running_Olden_llc_time.png"></a><br> -Execution time for the LLC generated executable -</td></tr> - -<tr> -<td align=center> -<a href="running_Olden_jit_time_large.png"><img width=480 height=360 border=0 src="running_Olden_jit_time.png"></a><br> -Execution time for program in the JIT -</td> -<td></td></tr> -</table> - - - - -<br><br><center> -<table border="0" cellspacing="0" cellpadding="2"><tr><td bgcolor="#000000"> -<table border="0" cellpadding="10" cellspacing="0"><tr><td bgcolor="#DDAA77" -<font size=+2 face=Verdana><b><a name="Programs">Program Tests</font></b> -</td></tr></table></td></tr></table></center><p> - -This section tests LLVM on a variety of programs in the test suite. This -includes benchmark suites like the Olden, McCat, Ptrdist, and SPEC benchmarks as -well as a few random programs with test inputs. This section is meant to track -how stable LLVM is as a whole. A failure in the execution of any test is marked -with an asterisk: `*'. The columns of the tables are:<p> - -<ol> -<li><a name="Program">Program</a> - The name of the program for that row.</li> -<li><a name="GCCAS">GCCAS</a> - Time to run LLVM optimizers on the program.</li> -<li><a name="Bytecode">Bytecode</a> - The size of the bytecode for the - program</li> -<li><a name="Instrs">Instrs</a> - The number of LLVM instructions in the - compiled bytecode</li> -<li><a name="LLC<br>compile">LLC compile</a> - The time taken compile with - LLC (the static backend)</li> -<li><a name="JIT<br>codegen">JIT codegen</a> - The amount of time spent in the - JIT itself, instead of executing the program.</li> -<li><a name="Machine<br>code">Machine code</a> - The number of bytes of machine - code generated by the JIT.</li> -<li><a name="GCC">GCC</a> - The time taken to execute the program when compiled - with GCC -O2.</li> -<li><a name="CBE">CBE</a> - The time taken to execute the program after - compilation through the C backend, compiled with -O2.</li> -<li><a name="LLC">LLC</a> - How long does the program generated by the static - backend LLC take to execute </li> -<li><a name="JIT">JIT</a> - The amount of time spent running the - program with the JIT; this includes the code generation phase (listed above) - and actually running the program.</li> -<li><a name="GCC/LLC">GCC/LLC</a> - The speed-up of the LLC output vs the native - GCC output: greater than 1 is a speedup, less than 1 is a slowdown.</li> -<li><a name="GCC/CBE">GCC/CBE</a> - The speed-up of the CBE output vs the native - GCC output: greater than 1 is a speedup, less than 1 is a slowdown.</li> -<li><a name="LLC-BETA">LLC-BETA</a> - How long does the program generated by the static - backend LLC take to execute the program, when compiled with new experimental - features. This is temporary, for tuning.</li> -</ol><p> - -A complete log of testing -<a href="$DATE-SingleSource-ProgramTest.txt.gz">SingleSource</a>, -<a href="$DATE-MultiSource-ProgramTest.txt.gz">MultiSource</a>, and -<a href="$DATE-External-ProgramTest.txt.gz">External</a> programs are -available for further analysis. - -<h2>Programs/External</h2> - -<center> -<table border="0" cellspacing="0" cellpadding="2"><tr><td bgcolor="#000000"> -$ExternalProgramsTable -</td></tr></table></center> - -<h2>Programs/MultiSource</h2> - -<center> -<table border="0" cellspacing="0" cellpadding="2"><tr><td bgcolor="#000000"> -$MultiSourceProgramsTable -</td></tr></table></center> - -<h2>Programs/SingleSource</h2> - -<center> -<table border="0" cellspacing="0" cellpadding="2"><tr><td bgcolor="#000000"> -$SingleSourceProgramsTable -</td></tr></table></center> - -</td></tr></html> - diff --git a/utils/TableGen/ARMDecoderEmitter.cpp b/utils/TableGen/ARMDecoderEmitter.cpp deleted file mode 100644 index 145b96d..0000000 --- a/utils/TableGen/ARMDecoderEmitter.cpp +++ /dev/null @@ -1,1790 +0,0 @@ -//===------------ ARMDecoderEmitter.cpp - Decoder Generator ---------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is part of the ARM Disassembler. -// It contains the tablegen backend that emits the decoder functions for ARM and -// Thumb. The disassembler core includes the auto-generated file, invokes the -// decoder functions, and builds up the MCInst based on the decoded Opcode. -// -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "arm-decoder-emitter" - -#include "ARMDecoderEmitter.h" -#include "CodeGenTarget.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/TableGen/Record.h" - -#include <vector> -#include <map> -#include <string> - -using namespace llvm; - -///////////////////////////////////////////////////// -// // -// Enums and Utilities for ARM Instruction Format // -// // -///////////////////////////////////////////////////// - -#define ARM_FORMATS \ - ENTRY(ARM_FORMAT_PSEUDO, 0) \ - ENTRY(ARM_FORMAT_MULFRM, 1) \ - ENTRY(ARM_FORMAT_BRFRM, 2) \ - ENTRY(ARM_FORMAT_BRMISCFRM, 3) \ - ENTRY(ARM_FORMAT_DPFRM, 4) \ - ENTRY(ARM_FORMAT_DPSOREGREGFRM, 5) \ - ENTRY(ARM_FORMAT_LDFRM, 6) \ - ENTRY(ARM_FORMAT_STFRM, 7) \ - ENTRY(ARM_FORMAT_LDMISCFRM, 8) \ - ENTRY(ARM_FORMAT_STMISCFRM, 9) \ - ENTRY(ARM_FORMAT_LDSTMULFRM, 10) \ - ENTRY(ARM_FORMAT_LDSTEXFRM, 11) \ - ENTRY(ARM_FORMAT_ARITHMISCFRM, 12) \ - ENTRY(ARM_FORMAT_SATFRM, 13) \ - ENTRY(ARM_FORMAT_EXTFRM, 14) \ - ENTRY(ARM_FORMAT_VFPUNARYFRM, 15) \ - ENTRY(ARM_FORMAT_VFPBINARYFRM, 16) \ - ENTRY(ARM_FORMAT_VFPCONV1FRM, 17) \ - ENTRY(ARM_FORMAT_VFPCONV2FRM, 18) \ - ENTRY(ARM_FORMAT_VFPCONV3FRM, 19) \ - ENTRY(ARM_FORMAT_VFPCONV4FRM, 20) \ - ENTRY(ARM_FORMAT_VFPCONV5FRM, 21) \ - ENTRY(ARM_FORMAT_VFPLDSTFRM, 22) \ - ENTRY(ARM_FORMAT_VFPLDSTMULFRM, 23) \ - ENTRY(ARM_FORMAT_VFPMISCFRM, 24) \ - ENTRY(ARM_FORMAT_THUMBFRM, 25) \ - ENTRY(ARM_FORMAT_MISCFRM, 26) \ - ENTRY(ARM_FORMAT_NEONGETLNFRM, 27) \ - ENTRY(ARM_FORMAT_NEONSETLNFRM, 28) \ - ENTRY(ARM_FORMAT_NEONDUPFRM, 29) \ - ENTRY(ARM_FORMAT_NLdSt, 30) \ - ENTRY(ARM_FORMAT_N1RegModImm, 31) \ - ENTRY(ARM_FORMAT_N2Reg, 32) \ - ENTRY(ARM_FORMAT_NVCVT, 33) \ - ENTRY(ARM_FORMAT_NVecDupLn, 34) \ - ENTRY(ARM_FORMAT_N2RegVecShL, 35) \ - ENTRY(ARM_FORMAT_N2RegVecShR, 36) \ - ENTRY(ARM_FORMAT_N3Reg, 37) \ - ENTRY(ARM_FORMAT_N3RegVecSh, 38) \ - ENTRY(ARM_FORMAT_NVecExtract, 39) \ - ENTRY(ARM_FORMAT_NVecMulScalar, 40) \ - ENTRY(ARM_FORMAT_NVTBL, 41) \ - ENTRY(ARM_FORMAT_DPSOREGIMMFRM, 42) - -// ARM instruction format specifies the encoding used by the instruction. -#define ENTRY(n, v) n = v, -typedef enum { - ARM_FORMATS - ARM_FORMAT_NA -} ARMFormat; -#undef ENTRY - -// Converts enum to const char*. -static const char *stringForARMFormat(ARMFormat form) { -#define ENTRY(n, v) case n: return #n; - switch(form) { - ARM_FORMATS - case ARM_FORMAT_NA: - default: - return ""; - } -#undef ENTRY -} - -enum { - IndexModeNone = 0, - IndexModePre = 1, - IndexModePost = 2, - IndexModeUpd = 3 -}; - -///////////////////////// -// // -// Utility functions // -// // -///////////////////////// - -/// byteFromBitsInit - Return the byte value from a BitsInit. -/// Called from getByteField(). -static uint8_t byteFromBitsInit(BitsInit &init) { - int width = init.getNumBits(); - - assert(width <= 8 && "Field is too large for uint8_t!"); - - int index; - uint8_t mask = 0x01; - - uint8_t ret = 0; - - for (index = 0; index < width; index++) { - if (static_cast<BitInit*>(init.getBit(index))->getValue()) - ret |= mask; - - mask <<= 1; - } - - return ret; -} - -static uint8_t getByteField(const Record &def, const char *str) { - BitsInit *bits = def.getValueAsBitsInit(str); - return byteFromBitsInit(*bits); -} - -static BitsInit &getBitsField(const Record &def, const char *str) { - BitsInit *bits = def.getValueAsBitsInit(str); - return *bits; -} - -/// sameStringExceptSuffix - Return true if the two strings differ only in RHS's -/// suffix. ("VST4d8", "VST4d8_UPD", "_UPD") as input returns true. -static -bool sameStringExceptSuffix(const StringRef LHS, const StringRef RHS, - const StringRef Suffix) { - - if (RHS.startswith(LHS) && RHS.endswith(Suffix)) - return RHS.size() == LHS.size() + Suffix.size(); - - return false; -} - -/// thumbInstruction - Determine whether we have a Thumb instruction. -/// See also ARMInstrFormats.td. -static bool thumbInstruction(uint8_t Form) { - return Form == ARM_FORMAT_THUMBFRM; -} - -// The set (BIT_TRUE, BIT_FALSE, BIT_UNSET) represents a ternary logic system -// for a bit value. -// -// BIT_UNFILTERED is used as the init value for a filter position. It is used -// only for filter processings. -typedef enum { - BIT_TRUE, // '1' - BIT_FALSE, // '0' - BIT_UNSET, // '?' - BIT_UNFILTERED // unfiltered -} bit_value_t; - -static bool ValueSet(bit_value_t V) { - return (V == BIT_TRUE || V == BIT_FALSE); -} -static bool ValueNotSet(bit_value_t V) { - return (V == BIT_UNSET); -} -static int Value(bit_value_t V) { - return ValueNotSet(V) ? -1 : (V == BIT_FALSE ? 0 : 1); -} -static bit_value_t bitFromBits(BitsInit &bits, unsigned index) { - if (BitInit *bit = dynamic_cast<BitInit*>(bits.getBit(index))) - return bit->getValue() ? BIT_TRUE : BIT_FALSE; - - // The bit is uninitialized. - return BIT_UNSET; -} -// Prints the bit value for each position. -static void dumpBits(raw_ostream &o, BitsInit &bits) { - unsigned index; - - for (index = bits.getNumBits(); index > 0; index--) { - switch (bitFromBits(bits, index - 1)) { - case BIT_TRUE: - o << "1"; - break; - case BIT_FALSE: - o << "0"; - break; - case BIT_UNSET: - o << "_"; - break; - default: - assert(0 && "unexpected return value from bitFromBits"); - } - } -} - -// Enums for the available target names. -typedef enum { - TARGET_ARM = 0, - TARGET_THUMB -} TARGET_NAME_t; - -// FIXME: Possibly auto-detected? -#define BIT_WIDTH 32 - -// Forward declaration. -class ARMFilterChooser; - -// Representation of the instruction to work on. -typedef bit_value_t insn_t[BIT_WIDTH]; - -/// Filter - Filter works with FilterChooser to produce the decoding tree for -/// the ISA. -/// -/// It is useful to think of a Filter as governing the switch stmts of the -/// decoding tree in a certain level. Each case stmt delegates to an inferior -/// FilterChooser to decide what further decoding logic to employ, or in another -/// words, what other remaining bits to look at. The FilterChooser eventually -/// chooses a best Filter to do its job. -/// -/// This recursive scheme ends when the number of Opcodes assigned to the -/// FilterChooser becomes 1 or if there is a conflict. A conflict happens when -/// the Filter/FilterChooser combo does not know how to distinguish among the -/// Opcodes assigned. -/// -/// An example of a conflict is -/// -/// Conflict: -/// 111101000.00........00010000.... -/// 111101000.00........0001........ -/// 1111010...00........0001........ -/// 1111010...00.................... -/// 1111010......................... -/// 1111............................ -/// ................................ -/// VST4q8a 111101000_00________00010000____ -/// VST4q8b 111101000_00________00010000____ -/// -/// The Debug output shows the path that the decoding tree follows to reach the -/// the conclusion that there is a conflict. VST4q8a is a vst4 to double-spaced -/// even registers, while VST4q8b is a vst4 to double-spaced odd regsisters. -/// -/// The encoding info in the .td files does not specify this meta information, -/// which could have been used by the decoder to resolve the conflict. The -/// decoder could try to decode the even/odd register numbering and assign to -/// VST4q8a or VST4q8b, but for the time being, the decoder chooses the "a" -/// version and return the Opcode since the two have the same Asm format string. -class ARMFilter { -protected: - ARMFilterChooser *Owner; // points to the FilterChooser who owns this filter - unsigned StartBit; // the starting bit position - unsigned NumBits; // number of bits to filter - bool Mixed; // a mixed region contains both set and unset bits - - // Map of well-known segment value to the set of uid's with that value. - std::map<uint64_t, std::vector<unsigned> > FilteredInstructions; - - // Set of uid's with non-constant segment values. - std::vector<unsigned> VariableInstructions; - - // Map of well-known segment value to its delegate. - std::map<unsigned, ARMFilterChooser*> FilterChooserMap; - - // Number of instructions which fall under FilteredInstructions category. - unsigned NumFiltered; - - // Keeps track of the last opcode in the filtered bucket. - unsigned LastOpcFiltered; - - // Number of instructions which fall under VariableInstructions category. - unsigned NumVariable; - -public: - unsigned getNumFiltered() { return NumFiltered; } - unsigned getNumVariable() { return NumVariable; } - unsigned getSingletonOpc() { - assert(NumFiltered == 1); - return LastOpcFiltered; - } - // Return the filter chooser for the group of instructions without constant - // segment values. - ARMFilterChooser &getVariableFC() { - assert(NumFiltered == 1); - assert(FilterChooserMap.size() == 1); - return *(FilterChooserMap.find((unsigned)-1)->second); - } - - ARMFilter(const ARMFilter &f); - ARMFilter(ARMFilterChooser &owner, unsigned startBit, unsigned numBits, - bool mixed); - - ~ARMFilter(); - - // Divides the decoding task into sub tasks and delegates them to the - // inferior FilterChooser's. - // - // A special case arises when there's only one entry in the filtered - // instructions. In order to unambiguously decode the singleton, we need to - // match the remaining undecoded encoding bits against the singleton. - void recurse(); - - // Emit code to decode instructions given a segment or segments of bits. - void emit(raw_ostream &o, unsigned &Indentation); - - // Returns the number of fanout produced by the filter. More fanout implies - // the filter distinguishes more categories of instructions. - unsigned usefulness() const; -}; // End of class Filter - -// These are states of our finite state machines used in FilterChooser's -// filterProcessor() which produces the filter candidates to use. -typedef enum { - ATTR_NONE, - ATTR_FILTERED, - ATTR_ALL_SET, - ATTR_ALL_UNSET, - ATTR_MIXED -} bitAttr_t; - -/// ARMFilterChooser - FilterChooser chooses the best filter among a set of Filters -/// in order to perform the decoding of instructions at the current level. -/// -/// Decoding proceeds from the top down. Based on the well-known encoding bits -/// of instructions available, FilterChooser builds up the possible Filters that -/// can further the task of decoding by distinguishing among the remaining -/// candidate instructions. -/// -/// Once a filter has been chosen, it is called upon to divide the decoding task -/// into sub-tasks and delegates them to its inferior FilterChoosers for further -/// processings. -/// -/// It is useful to think of a Filter as governing the switch stmts of the -/// decoding tree. And each case is delegated to an inferior FilterChooser to -/// decide what further remaining bits to look at. -class ARMFilterChooser { - static TARGET_NAME_t TargetName; - -protected: - friend class ARMFilter; - - // Vector of codegen instructions to choose our filter. - const std::vector<const CodeGenInstruction*> &AllInstructions; - - // Vector of uid's for this filter chooser to work on. - const std::vector<unsigned> Opcodes; - - // Vector of candidate filters. - std::vector<ARMFilter> Filters; - - // Array of bit values passed down from our parent. - // Set to all BIT_UNFILTERED's for Parent == NULL. - bit_value_t FilterBitValues[BIT_WIDTH]; - - // Links to the FilterChooser above us in the decoding tree. - ARMFilterChooser *Parent; - - // Index of the best filter from Filters. - int BestIndex; - -public: - static void setTargetName(TARGET_NAME_t tn) { TargetName = tn; } - - ARMFilterChooser(const ARMFilterChooser &FC) : - AllInstructions(FC.AllInstructions), Opcodes(FC.Opcodes), - Filters(FC.Filters), Parent(FC.Parent), BestIndex(FC.BestIndex) { - memcpy(FilterBitValues, FC.FilterBitValues, sizeof(FilterBitValues)); - } - - ARMFilterChooser(const std::vector<const CodeGenInstruction*> &Insts, - const std::vector<unsigned> &IDs) : - AllInstructions(Insts), Opcodes(IDs), Filters(), Parent(NULL), - BestIndex(-1) { - for (unsigned i = 0; i < BIT_WIDTH; ++i) - FilterBitValues[i] = BIT_UNFILTERED; - - doFilter(); - } - - ARMFilterChooser(const std::vector<const CodeGenInstruction*> &Insts, - const std::vector<unsigned> &IDs, - bit_value_t (&ParentFilterBitValues)[BIT_WIDTH], - ARMFilterChooser &parent) : - AllInstructions(Insts), Opcodes(IDs), Filters(), Parent(&parent), - BestIndex(-1) { - for (unsigned i = 0; i < BIT_WIDTH; ++i) - FilterBitValues[i] = ParentFilterBitValues[i]; - - doFilter(); - } - - // The top level filter chooser has NULL as its parent. - bool isTopLevel() { return Parent == NULL; } - - // This provides an opportunity for target specific code emission. - void emitTopHook(raw_ostream &o); - - // Emit the top level typedef and decodeInstruction() function. - void emitTop(raw_ostream &o, unsigned &Indentation); - - // This provides an opportunity for target specific code emission after - // emitTop(). - void emitBot(raw_ostream &o, unsigned &Indentation); - -protected: - // Populates the insn given the uid. - void insnWithID(insn_t &Insn, unsigned Opcode) const { - if (AllInstructions[Opcode]->isPseudo) - return; - - BitsInit &Bits = getBitsField(*AllInstructions[Opcode]->TheDef, "Inst"); - - for (unsigned i = 0; i < BIT_WIDTH; ++i) - Insn[i] = bitFromBits(Bits, i); - - // Set Inst{21} to 1 (wback) when IndexModeBits == IndexModeUpd. - Record *R = AllInstructions[Opcode]->TheDef; - if (R->getValue("IndexModeBits") && - getByteField(*R, "IndexModeBits") == IndexModeUpd) - Insn[21] = BIT_TRUE; - } - - // Returns the record name. - const std::string &nameWithID(unsigned Opcode) const { - return AllInstructions[Opcode]->TheDef->getName(); - } - - // Populates the field of the insn given the start position and the number of - // consecutive bits to scan for. - // - // Returns false if there exists any uninitialized bit value in the range. - // Returns true, otherwise. - bool fieldFromInsn(uint64_t &Field, insn_t &Insn, unsigned StartBit, - unsigned NumBits) const; - - /// dumpFilterArray - dumpFilterArray prints out debugging info for the given - /// filter array as a series of chars. - void dumpFilterArray(raw_ostream &o, bit_value_t (&filter)[BIT_WIDTH]); - - /// dumpStack - dumpStack traverses the filter chooser chain and calls - /// dumpFilterArray on each filter chooser up to the top level one. - void dumpStack(raw_ostream &o, const char *prefix); - - ARMFilter &bestFilter() { - assert(BestIndex != -1 && "BestIndex not set"); - return Filters[BestIndex]; - } - - // Called from Filter::recurse() when singleton exists. For debug purpose. - void SingletonExists(unsigned Opc); - - bool PositionFiltered(unsigned i) { - return ValueSet(FilterBitValues[i]); - } - - // Calculates the island(s) needed to decode the instruction. - // This returns a lit of undecoded bits of an instructions, for example, - // Inst{20} = 1 && Inst{3-0} == 0b1111 represents two islands of yet-to-be - // decoded bits in order to verify that the instruction matches the Opcode. - unsigned getIslands(std::vector<unsigned> &StartBits, - std::vector<unsigned> &EndBits, std::vector<uint64_t> &FieldVals, - insn_t &Insn); - - // The purpose of this function is for the API client to detect possible - // Load/Store Coprocessor instructions. If the coprocessor number is of - // the instruction is either 10 or 11, the decoder should not report the - // instruction as LDC/LDC2/STC/STC2, but should match against Advanced SIMD or - // VFP instructions. - bool LdStCopEncoding1(unsigned Opc) { - const std::string &Name = nameWithID(Opc); - if (Name == "LDC_OFFSET" || Name == "LDC_OPTION" || - Name == "LDC_POST" || Name == "LDC_PRE" || - Name == "LDCL_OFFSET" || Name == "LDCL_OPTION" || - Name == "LDCL_POST" || Name == "LDCL_PRE" || - Name == "STC_OFFSET" || Name == "STC_OPTION" || - Name == "STC_POST" || Name == "STC_PRE" || - Name == "STCL_OFFSET" || Name == "STCL_OPTION" || - Name == "STCL_POST" || Name == "STCL_PRE") - return true; - else - return false; - } - - // Emits code to decode the singleton. Return true if we have matched all the - // well-known bits. - bool emitSingletonDecoder(raw_ostream &o, unsigned &Indentation,unsigned Opc); - - // Emits code to decode the singleton, and then to decode the rest. - void emitSingletonDecoder(raw_ostream &o, unsigned &Indentation, - ARMFilter &Best); - - // Assign a single filter and run with it. - void runSingleFilter(ARMFilterChooser &owner, unsigned startBit, - unsigned numBit, bool mixed); - - // reportRegion is a helper function for filterProcessor to mark a region as - // eligible for use as a filter region. - void reportRegion(bitAttr_t RA, unsigned StartBit, unsigned BitIndex, - bool AllowMixed); - - // FilterProcessor scans the well-known encoding bits of the instructions and - // builds up a list of candidate filters. It chooses the best filter and - // recursively descends down the decoding tree. - bool filterProcessor(bool AllowMixed, bool Greedy = true); - - // Decides on the best configuration of filter(s) to use in order to decode - // the instructions. A conflict of instructions may occur, in which case we - // dump the conflict set to the standard error. - void doFilter(); - - // Emits code to decode our share of instructions. Returns true if the - // emitted code causes a return, which occurs if we know how to decode - // the instruction at this level or the instruction is not decodeable. - bool emit(raw_ostream &o, unsigned &Indentation); -}; - -/////////////////////////// -// // -// Filter Implmenetation // -// // -/////////////////////////// - -ARMFilter::ARMFilter(const ARMFilter &f) : - Owner(f.Owner), StartBit(f.StartBit), NumBits(f.NumBits), Mixed(f.Mixed), - FilteredInstructions(f.FilteredInstructions), - VariableInstructions(f.VariableInstructions), - FilterChooserMap(f.FilterChooserMap), NumFiltered(f.NumFiltered), - LastOpcFiltered(f.LastOpcFiltered), NumVariable(f.NumVariable) { -} - -ARMFilter::ARMFilter(ARMFilterChooser &owner, unsigned startBit, unsigned numBits, - bool mixed) : Owner(&owner), StartBit(startBit), NumBits(numBits), - Mixed(mixed) { - assert(StartBit + NumBits - 1 < BIT_WIDTH); - - NumFiltered = 0; - LastOpcFiltered = 0; - NumVariable = 0; - - for (unsigned i = 0, e = Owner->Opcodes.size(); i != e; ++i) { - insn_t Insn; - - // Populates the insn given the uid. - Owner->insnWithID(Insn, Owner->Opcodes[i]); - - uint64_t Field; - // Scans the segment for possibly well-specified encoding bits. - bool ok = Owner->fieldFromInsn(Field, Insn, StartBit, NumBits); - - if (ok) { - // The encoding bits are well-known. Lets add the uid of the - // instruction into the bucket keyed off the constant field value. - LastOpcFiltered = Owner->Opcodes[i]; - FilteredInstructions[Field].push_back(LastOpcFiltered); - ++NumFiltered; - } else { - // Some of the encoding bit(s) are unspecfied. This contributes to - // one additional member of "Variable" instructions. - VariableInstructions.push_back(Owner->Opcodes[i]); - ++NumVariable; - } - } - - assert((FilteredInstructions.size() + VariableInstructions.size() > 0) - && "Filter returns no instruction categories"); -} - -ARMFilter::~ARMFilter() { - std::map<unsigned, ARMFilterChooser*>::iterator filterIterator; - for (filterIterator = FilterChooserMap.begin(); - filterIterator != FilterChooserMap.end(); - filterIterator++) { - delete filterIterator->second; - } -} - -// Divides the decoding task into sub tasks and delegates them to the -// inferior FilterChooser's. -// -// A special case arises when there's only one entry in the filtered -// instructions. In order to unambiguously decode the singleton, we need to -// match the remaining undecoded encoding bits against the singleton. -void ARMFilter::recurse() { - std::map<uint64_t, std::vector<unsigned> >::const_iterator mapIterator; - - bit_value_t BitValueArray[BIT_WIDTH]; - // Starts by inheriting our parent filter chooser's filter bit values. - memcpy(BitValueArray, Owner->FilterBitValues, sizeof(BitValueArray)); - - unsigned bitIndex; - - if (VariableInstructions.size()) { - // Conservatively marks each segment position as BIT_UNSET. - for (bitIndex = 0; bitIndex < NumBits; bitIndex++) - BitValueArray[StartBit + bitIndex] = BIT_UNSET; - - // Delegates to an inferior filter chooser for further processing on this - // group of instructions whose segment values are variable. - FilterChooserMap.insert(std::pair<unsigned, ARMFilterChooser*>( - (unsigned)-1, - new ARMFilterChooser(Owner->AllInstructions, - VariableInstructions, - BitValueArray, - *Owner) - )); - } - - // No need to recurse for a singleton filtered instruction. - // See also Filter::emit(). - if (getNumFiltered() == 1) { - //Owner->SingletonExists(LastOpcFiltered); - assert(FilterChooserMap.size() == 1); - return; - } - - // Otherwise, create sub choosers. - for (mapIterator = FilteredInstructions.begin(); - mapIterator != FilteredInstructions.end(); - mapIterator++) { - - // Marks all the segment positions with either BIT_TRUE or BIT_FALSE. - for (bitIndex = 0; bitIndex < NumBits; bitIndex++) { - if (mapIterator->first & (1ULL << bitIndex)) - BitValueArray[StartBit + bitIndex] = BIT_TRUE; - else - BitValueArray[StartBit + bitIndex] = BIT_FALSE; - } - - // Delegates to an inferior filter chooser for further processing on this - // category of instructions. - FilterChooserMap.insert(std::pair<unsigned, ARMFilterChooser*>( - mapIterator->first, - new ARMFilterChooser(Owner->AllInstructions, - mapIterator->second, - BitValueArray, - *Owner) - )); - } -} - -// Emit code to decode instructions given a segment or segments of bits. -void ARMFilter::emit(raw_ostream &o, unsigned &Indentation) { - o.indent(Indentation) << "// Check Inst{"; - - if (NumBits > 1) - o << (StartBit + NumBits - 1) << '-'; - - o << StartBit << "} ...\n"; - - o.indent(Indentation) << "switch (fieldFromInstruction(insn, " - << StartBit << ", " << NumBits << ")) {\n"; - - std::map<unsigned, ARMFilterChooser*>::iterator filterIterator; - - bool DefaultCase = false; - for (filterIterator = FilterChooserMap.begin(); - filterIterator != FilterChooserMap.end(); - filterIterator++) { - - // Field value -1 implies a non-empty set of variable instructions. - // See also recurse(). - if (filterIterator->first == (unsigned)-1) { - DefaultCase = true; - - o.indent(Indentation) << "default:\n"; - o.indent(Indentation) << " break; // fallthrough\n"; - - // Closing curly brace for the switch statement. - // This is unconventional because we want the default processing to be - // performed for the fallthrough cases as well, i.e., when the "cases" - // did not prove a decoded instruction. - o.indent(Indentation) << "}\n"; - - } else - o.indent(Indentation) << "case " << filterIterator->first << ":\n"; - - // We arrive at a category of instructions with the same segment value. - // Now delegate to the sub filter chooser for further decodings. - // The case may fallthrough, which happens if the remaining well-known - // encoding bits do not match exactly. - if (!DefaultCase) { ++Indentation; ++Indentation; } - - bool finished = filterIterator->second->emit(o, Indentation); - // For top level default case, there's no need for a break statement. - if (Owner->isTopLevel() && DefaultCase) - break; - if (!finished) - o.indent(Indentation) << "break;\n"; - - if (!DefaultCase) { --Indentation; --Indentation; } - } - - // If there is no default case, we still need to supply a closing brace. - if (!DefaultCase) { - // Closing curly brace for the switch statement. - o.indent(Indentation) << "}\n"; - } -} - -// Returns the number of fanout produced by the filter. More fanout implies -// the filter distinguishes more categories of instructions. -unsigned ARMFilter::usefulness() const { - if (VariableInstructions.size()) - return FilteredInstructions.size(); - else - return FilteredInstructions.size() + 1; -} - -////////////////////////////////// -// // -// Filterchooser Implementation // -// // -////////////////////////////////// - -// Define the symbol here. -TARGET_NAME_t ARMFilterChooser::TargetName; - -// This provides an opportunity for target specific code emission. -void ARMFilterChooser::emitTopHook(raw_ostream &o) { - if (TargetName == TARGET_ARM) { - // Emit code that references the ARMFormat data type. - o << "static const ARMFormat ARMFormats[] = {\n"; - for (unsigned i = 0, e = AllInstructions.size(); i != e; ++i) { - const Record &Def = *(AllInstructions[i]->TheDef); - const std::string &Name = Def.getName(); - if (Def.isSubClassOf("InstARM") || Def.isSubClassOf("InstThumb")) - o.indent(2) << - stringForARMFormat((ARMFormat)getByteField(Def, "Form")); - else - o << " ARM_FORMAT_NA"; - - o << ",\t// Inst #" << i << " = " << Name << '\n'; - } - o << " ARM_FORMAT_NA\t// Unreachable.\n"; - o << "};\n\n"; - } -} - -// Emit the top level typedef and decodeInstruction() function. -void ARMFilterChooser::emitTop(raw_ostream &o, unsigned &Indentation) { - // Run the target specific emit hook. - emitTopHook(o); - - switch (BIT_WIDTH) { - case 8: - o.indent(Indentation) << "typedef uint8_t field_t;\n"; - break; - case 16: - o.indent(Indentation) << "typedef uint16_t field_t;\n"; - break; - case 32: - o.indent(Indentation) << "typedef uint32_t field_t;\n"; - break; - case 64: - o.indent(Indentation) << "typedef uint64_t field_t;\n"; - break; - default: - assert(0 && "Unexpected instruction size!"); - } - - o << '\n'; - - o.indent(Indentation) << "static field_t " << - "fieldFromInstruction(field_t insn, unsigned startBit, unsigned numBits)\n"; - - o.indent(Indentation) << "{\n"; - - ++Indentation; ++Indentation; - o.indent(Indentation) << "assert(startBit + numBits <= " << BIT_WIDTH - << " && \"Instruction field out of bounds!\");\n"; - o << '\n'; - o.indent(Indentation) << "field_t fieldMask;\n"; - o << '\n'; - o.indent(Indentation) << "if (numBits == " << BIT_WIDTH << ")\n"; - - ++Indentation; ++Indentation; - o.indent(Indentation) << "fieldMask = (field_t)-1;\n"; - --Indentation; --Indentation; - - o.indent(Indentation) << "else\n"; - - ++Indentation; ++Indentation; - o.indent(Indentation) << "fieldMask = ((1 << numBits) - 1) << startBit;\n"; - --Indentation; --Indentation; - - o << '\n'; - o.indent(Indentation) << "return (insn & fieldMask) >> startBit;\n"; - --Indentation; --Indentation; - - o.indent(Indentation) << "}\n"; - - o << '\n'; - - o.indent(Indentation) <<"static uint16_t decodeInstruction(field_t insn) {\n"; - - ++Indentation; ++Indentation; - // Emits code to decode the instructions. - emit(o, Indentation); - - o << '\n'; - o.indent(Indentation) << "return 0;\n"; - --Indentation; --Indentation; - - o.indent(Indentation) << "}\n"; - - o << '\n'; -} - -// This provides an opportunity for target specific code emission after -// emitTop(). -void ARMFilterChooser::emitBot(raw_ostream &o, unsigned &Indentation) { - if (TargetName != TARGET_THUMB) return; - - // Emit code that decodes the Thumb ISA. - o.indent(Indentation) - << "static uint16_t decodeThumbInstruction(field_t insn) {\n"; - - ++Indentation; ++Indentation; - - // Emits code to decode the instructions. - emit(o, Indentation); - - o << '\n'; - o.indent(Indentation) << "return 0;\n"; - - --Indentation; --Indentation; - - o.indent(Indentation) << "}\n"; -} - -// Populates the field of the insn given the start position and the number of -// consecutive bits to scan for. -// -// Returns false if and on the first uninitialized bit value encountered. -// Returns true, otherwise. -bool ARMFilterChooser::fieldFromInsn(uint64_t &Field, insn_t &Insn, - unsigned StartBit, unsigned NumBits) const { - Field = 0; - - for (unsigned i = 0; i < NumBits; ++i) { - if (Insn[StartBit + i] == BIT_UNSET) - return false; - - if (Insn[StartBit + i] == BIT_TRUE) - Field = Field | (1ULL << i); - } - - return true; -} - -/// dumpFilterArray - dumpFilterArray prints out debugging info for the given -/// filter array as a series of chars. -void ARMFilterChooser::dumpFilterArray(raw_ostream &o, - bit_value_t (&filter)[BIT_WIDTH]) { - unsigned bitIndex; - - for (bitIndex = BIT_WIDTH; bitIndex > 0; bitIndex--) { - switch (filter[bitIndex - 1]) { - case BIT_UNFILTERED: - o << "."; - break; - case BIT_UNSET: - o << "_"; - break; - case BIT_TRUE: - o << "1"; - break; - case BIT_FALSE: - o << "0"; - break; - } - } -} - -/// dumpStack - dumpStack traverses the filter chooser chain and calls -/// dumpFilterArray on each filter chooser up to the top level one. -void ARMFilterChooser::dumpStack(raw_ostream &o, const char *prefix) { - ARMFilterChooser *current = this; - - while (current) { - o << prefix; - dumpFilterArray(o, current->FilterBitValues); - o << '\n'; - current = current->Parent; - } -} - -// Called from Filter::recurse() when singleton exists. For debug purpose. -void ARMFilterChooser::SingletonExists(unsigned Opc) { - 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 -// decoded bits in order to verify that the instruction matches the Opcode. -unsigned ARMFilterChooser::getIslands(std::vector<unsigned> &StartBits, - std::vector<unsigned> &EndBits, std::vector<uint64_t> &FieldVals, - insn_t &Insn) { - unsigned Num, BitNo; - Num = BitNo = 0; - - uint64_t FieldVal = 0; - - // 0: Init - // 1: Water (the bit value does not affect decoding) - // 2: Island (well-known bit value needed for decoding) - int State = 0; - int Val = -1; - - for (unsigned i = 0; i < BIT_WIDTH; ++i) { - Val = Value(Insn[i]); - bool Filtered = PositionFiltered(i); - switch (State) { - default: - assert(0 && "Unreachable code!"); - break; - case 0: - case 1: - if (Filtered || Val == -1) - State = 1; // Still in Water - else { - State = 2; // Into the Island - BitNo = 0; - StartBits.push_back(i); - FieldVal = Val; - } - break; - case 2: - if (Filtered || Val == -1) { - State = 1; // Into the Water - EndBits.push_back(i - 1); - FieldVals.push_back(FieldVal); - ++Num; - } else { - State = 2; // Still in Island - ++BitNo; - FieldVal = FieldVal | Val << BitNo; - } - break; - } - } - // If we are still in Island after the loop, do some housekeeping. - if (State == 2) { - EndBits.push_back(BIT_WIDTH - 1); - FieldVals.push_back(FieldVal); - ++Num; - } - - assert(StartBits.size() == Num && EndBits.size() == Num && - FieldVals.size() == Num); - return Num; -} - -// Emits code to decode the singleton. Return true if we have matched all the -// well-known bits. -bool ARMFilterChooser::emitSingletonDecoder(raw_ostream &o, unsigned &Indentation, - unsigned Opc) { - std::vector<unsigned> StartBits; - std::vector<unsigned> EndBits; - std::vector<uint64_t> FieldVals; - insn_t Insn; - insnWithID(Insn, Opc); - - // This provides a good opportunity to check for possible Ld/St Coprocessor - // Opcode and escapes if the coproc # is either 10 or 11. It is a NEON/VFP - // instruction is disguise. - if (TargetName == TARGET_ARM && LdStCopEncoding1(Opc)) { - o.indent(Indentation); - // A8.6.51 & A8.6.188 - // If coproc = 0b101?, i.e, slice(insn, 11, 8) = 10 or 11, escape. - o << "if (fieldFromInstruction(insn, 9, 3) == 5) break; // fallthrough\n"; - } - - // Look for islands of undecoded bits of the singleton. - getIslands(StartBits, EndBits, FieldVals, Insn); - - unsigned Size = StartBits.size(); - unsigned I, NumBits; - - // If we have matched all the well-known bits, just issue a return. - if (Size == 0) { - o.indent(Indentation) << "return " << Opc << "; // " << nameWithID(Opc) - << '\n'; - return true; - } - - // Otherwise, there are more decodings to be done! - - // Emit code to match the island(s) for the singleton. - o.indent(Indentation) << "// Check "; - - for (I = Size; I != 0; --I) { - o << "Inst{" << EndBits[I-1] << '-' << StartBits[I-1] << "} "; - if (I > 1) - o << "&& "; - else - o << "for singleton decoding...\n"; - } - - o.indent(Indentation) << "if ("; - - for (I = Size; I != 0; --I) { - NumBits = EndBits[I-1] - StartBits[I-1] + 1; - o << "fieldFromInstruction(insn, " << StartBits[I-1] << ", " << NumBits - << ") == " << FieldVals[I-1]; - if (I > 1) - o << " && "; - else - o << ")\n"; - } - - o.indent(Indentation) << " return " << Opc << "; // " << nameWithID(Opc) - << '\n'; - - return false; -} - -// Emits code to decode the singleton, and then to decode the rest. -void ARMFilterChooser::emitSingletonDecoder(raw_ostream &o, - unsigned &Indentation, - ARMFilter &Best) { - - unsigned Opc = Best.getSingletonOpc(); - - emitSingletonDecoder(o, Indentation, Opc); - - // Emit code for the rest. - o.indent(Indentation) << "else\n"; - - Indentation += 2; - Best.getVariableFC().emit(o, Indentation); - Indentation -= 2; -} - -// Assign a single filter and run with it. Top level API client can initialize -// with a single filter to start the filtering process. -void ARMFilterChooser::runSingleFilter(ARMFilterChooser &owner, - unsigned startBit, - unsigned numBit, bool mixed) { - Filters.clear(); - ARMFilter F(*this, startBit, numBit, true); - Filters.push_back(F); - BestIndex = 0; // Sole Filter instance to choose from. - bestFilter().recurse(); -} - -// reportRegion is a helper function for filterProcessor to mark a region as -// eligible for use as a filter region. -void ARMFilterChooser::reportRegion(bitAttr_t RA, unsigned StartBit, - unsigned BitIndex, bool AllowMixed) { - if (RA == ATTR_MIXED && AllowMixed) - Filters.push_back(ARMFilter(*this, StartBit, BitIndex - StartBit, true)); - else if (RA == ATTR_ALL_SET && !AllowMixed) - Filters.push_back(ARMFilter(*this, StartBit, BitIndex - StartBit, false)); -} - -// FilterProcessor scans the well-known encoding bits of the instructions and -// builds up a list of candidate filters. It chooses the best filter and -// recursively descends down the decoding tree. -bool ARMFilterChooser::filterProcessor(bool AllowMixed, bool Greedy) { - Filters.clear(); - BestIndex = -1; - unsigned numInstructions = Opcodes.size(); - - assert(numInstructions && "Filter created with no instructions"); - - // No further filtering is necessary. - if (numInstructions == 1) - return true; - - // Heuristics. See also doFilter()'s "Heuristics" comment when num of - // instructions is 3. - if (AllowMixed && !Greedy) { - assert(numInstructions == 3); - - for (unsigned i = 0; i < Opcodes.size(); ++i) { - std::vector<unsigned> StartBits; - std::vector<unsigned> EndBits; - std::vector<uint64_t> FieldVals; - insn_t Insn; - - insnWithID(Insn, Opcodes[i]); - - // Look for islands of undecoded bits of any instruction. - if (getIslands(StartBits, EndBits, FieldVals, Insn) > 0) { - // Found an instruction with island(s). Now just assign a filter. - runSingleFilter(*this, StartBits[0], EndBits[0] - StartBits[0] + 1, - true); - return true; - } - } - } - - unsigned BitIndex, InsnIndex; - - // We maintain BIT_WIDTH copies of the bitAttrs automaton. - // The automaton consumes the corresponding bit from each - // instruction. - // - // Input symbols: 0, 1, and _ (unset). - // States: NONE, FILTERED, ALL_SET, ALL_UNSET, and MIXED. - // Initial state: NONE. - // - // (NONE) ------- [01] -> (ALL_SET) - // (NONE) ------- _ ----> (ALL_UNSET) - // (ALL_SET) ---- [01] -> (ALL_SET) - // (ALL_SET) ---- _ ----> (MIXED) - // (ALL_UNSET) -- [01] -> (MIXED) - // (ALL_UNSET) -- _ ----> (ALL_UNSET) - // (MIXED) ------ . ----> (MIXED) - // (FILTERED)---- . ----> (FILTERED) - - bitAttr_t bitAttrs[BIT_WIDTH]; - - // FILTERED bit positions provide no entropy and are not worthy of pursuing. - // Filter::recurse() set either BIT_TRUE or BIT_FALSE for each position. - for (BitIndex = 0; BitIndex < BIT_WIDTH; ++BitIndex) - if (FilterBitValues[BitIndex] == BIT_TRUE || - FilterBitValues[BitIndex] == BIT_FALSE) - bitAttrs[BitIndex] = ATTR_FILTERED; - else - bitAttrs[BitIndex] = ATTR_NONE; - - for (InsnIndex = 0; InsnIndex < numInstructions; ++InsnIndex) { - insn_t insn; - - insnWithID(insn, Opcodes[InsnIndex]); - - for (BitIndex = 0; BitIndex < BIT_WIDTH; ++BitIndex) { - switch (bitAttrs[BitIndex]) { - case ATTR_NONE: - if (insn[BitIndex] == BIT_UNSET) - bitAttrs[BitIndex] = ATTR_ALL_UNSET; - else - bitAttrs[BitIndex] = ATTR_ALL_SET; - break; - case ATTR_ALL_SET: - if (insn[BitIndex] == BIT_UNSET) - bitAttrs[BitIndex] = ATTR_MIXED; - break; - case ATTR_ALL_UNSET: - if (insn[BitIndex] != BIT_UNSET) - bitAttrs[BitIndex] = ATTR_MIXED; - break; - case ATTR_MIXED: - case ATTR_FILTERED: - break; - } - } - } - - // The regionAttr automaton consumes the bitAttrs automatons' state, - // lowest-to-highest. - // - // Input symbols: F(iltered), (all_)S(et), (all_)U(nset), M(ixed) - // States: NONE, ALL_SET, MIXED - // Initial state: NONE - // - // (NONE) ----- F --> (NONE) - // (NONE) ----- S --> (ALL_SET) ; and set region start - // (NONE) ----- U --> (NONE) - // (NONE) ----- M --> (MIXED) ; and set region start - // (ALL_SET) -- F --> (NONE) ; and report an ALL_SET region - // (ALL_SET) -- S --> (ALL_SET) - // (ALL_SET) -- U --> (NONE) ; and report an ALL_SET region - // (ALL_SET) -- M --> (MIXED) ; and report an ALL_SET region - // (MIXED) ---- F --> (NONE) ; and report a MIXED region - // (MIXED) ---- S --> (ALL_SET) ; and report a MIXED region - // (MIXED) ---- U --> (NONE) ; and report a MIXED region - // (MIXED) ---- M --> (MIXED) - - bitAttr_t RA = ATTR_NONE; - unsigned StartBit = 0; - - for (BitIndex = 0; BitIndex < BIT_WIDTH; BitIndex++) { - bitAttr_t bitAttr = bitAttrs[BitIndex]; - - assert(bitAttr != ATTR_NONE && "Bit without attributes"); - - switch (RA) { - case ATTR_NONE: - switch (bitAttr) { - case ATTR_FILTERED: - break; - case ATTR_ALL_SET: - StartBit = BitIndex; - RA = ATTR_ALL_SET; - break; - case ATTR_ALL_UNSET: - break; - case ATTR_MIXED: - StartBit = BitIndex; - RA = ATTR_MIXED; - break; - default: - assert(0 && "Unexpected bitAttr!"); - } - break; - case ATTR_ALL_SET: - switch (bitAttr) { - case ATTR_FILTERED: - reportRegion(RA, StartBit, BitIndex, AllowMixed); - RA = ATTR_NONE; - break; - case ATTR_ALL_SET: - break; - case ATTR_ALL_UNSET: - reportRegion(RA, StartBit, BitIndex, AllowMixed); - RA = ATTR_NONE; - break; - case ATTR_MIXED: - reportRegion(RA, StartBit, BitIndex, AllowMixed); - StartBit = BitIndex; - RA = ATTR_MIXED; - break; - default: - assert(0 && "Unexpected bitAttr!"); - } - break; - case ATTR_MIXED: - switch (bitAttr) { - case ATTR_FILTERED: - reportRegion(RA, StartBit, BitIndex, AllowMixed); - StartBit = BitIndex; - RA = ATTR_NONE; - break; - case ATTR_ALL_SET: - reportRegion(RA, StartBit, BitIndex, AllowMixed); - StartBit = BitIndex; - RA = ATTR_ALL_SET; - break; - case ATTR_ALL_UNSET: - reportRegion(RA, StartBit, BitIndex, AllowMixed); - RA = ATTR_NONE; - break; - case ATTR_MIXED: - break; - default: - assert(0 && "Unexpected bitAttr!"); - } - break; - case ATTR_ALL_UNSET: - assert(0 && "regionAttr state machine has no ATTR_UNSET state"); - case ATTR_FILTERED: - assert(0 && "regionAttr state machine has no ATTR_FILTERED state"); - } - } - - // At the end, if we're still in ALL_SET or MIXED states, report a region - switch (RA) { - case ATTR_NONE: - break; - case ATTR_FILTERED: - break; - case ATTR_ALL_SET: - reportRegion(RA, StartBit, BitIndex, AllowMixed); - break; - case ATTR_ALL_UNSET: - break; - case ATTR_MIXED: - reportRegion(RA, StartBit, BitIndex, AllowMixed); - break; - } - - // We have finished with the filter processings. Now it's time to choose - // the best performing filter. - BestIndex = 0; - bool AllUseless = true; - unsigned BestScore = 0; - - for (unsigned i = 0, e = Filters.size(); i != e; ++i) { - unsigned Usefulness = Filters[i].usefulness(); - - if (Usefulness) - AllUseless = false; - - if (Usefulness > BestScore) { - BestIndex = i; - BestScore = Usefulness; - } - } - - if (!AllUseless) - bestFilter().recurse(); - - return !AllUseless; -} // end of FilterChooser::filterProcessor(bool) - -// Decides on the best configuration of filter(s) to use in order to decode -// the instructions. A conflict of instructions may occur, in which case we -// dump the conflict set to the standard error. -void ARMFilterChooser::doFilter() { - unsigned Num = Opcodes.size(); - assert(Num && "FilterChooser created with no instructions"); - - // Heuristics: Use Inst{31-28} as the top level filter for ARM ISA. - if (TargetName == TARGET_ARM && Parent == NULL) { - runSingleFilter(*this, 28, 4, false); - return; - } - - // Try regions of consecutive known bit values first. - if (filterProcessor(false)) - return; - - // Then regions of mixed bits (both known and unitialized bit values allowed). - if (filterProcessor(true)) - return; - - // Heuristics to cope with conflict set {t2CMPrs, t2SUBSrr, t2SUBSrs} where - // no single instruction for the maximum ATTR_MIXED region Inst{14-4} has a - // well-known encoding pattern. In such case, we backtrack and scan for the - // the very first consecutive ATTR_ALL_SET region and assign a filter to it. - if (Num == 3 && filterProcessor(true, false)) - return; - - // If we come to here, the instruction decoding has failed. - // Set the BestIndex to -1 to indicate so. - BestIndex = -1; -} - -// Emits code to decode our share of instructions. Returns true if the -// emitted code causes a return, which occurs if we know how to decode -// the instruction at this level or the instruction is not decodeable. -bool ARMFilterChooser::emit(raw_ostream &o, unsigned &Indentation) { - if (Opcodes.size() == 1) - // There is only one instruction in the set, which is great! - // Call emitSingletonDecoder() to see whether there are any remaining - // encodings bits. - return emitSingletonDecoder(o, Indentation, Opcodes[0]); - - // Choose the best filter to do the decodings! - if (BestIndex != -1) { - ARMFilter &Best = bestFilter(); - if (Best.getNumFiltered() == 1) - emitSingletonDecoder(o, Indentation, Best); - else - bestFilter().emit(o, Indentation); - return false; - } - - // If we reach here, there is a conflict in decoding. Let's resolve the known - // conflicts! - if ((TargetName == TARGET_ARM || TargetName == TARGET_THUMB) && - Opcodes.size() == 2) { - // Resolve the known conflict sets: - // - // 1. source registers are identical => VMOVDneon; otherwise => VORRd - // 2. source registers are identical => VMOVQ; otherwise => VORRq - // 3. LDR, LDRcp => return LDR for now. - // FIXME: How can we distinguish between LDR and LDRcp? Do we need to? - // 4. tLDMIA, tLDMIA_UPD => Rn = Inst{10-8}, reglist = Inst{7-0}, - // wback = registers<Rn> = 0 - // NOTE: (tLDM, tLDM_UPD) resolution must come before Advanced SIMD - // addressing mode resolution!!! - // 5. VLD[234]LN*/VST[234]LN* vs. VLD[234]LN*_UPD/VST[234]LN*_UPD conflicts - // are resolved returning the non-UPD versions of the instructions if the - // Rm field, i.e., Inst{3-0} is 0b1111. This is specified in A7.7.1 - // Advanced SIMD addressing mode. - const std::string &name1 = nameWithID(Opcodes[0]); - const std::string &name2 = nameWithID(Opcodes[1]); - if ((name1 == "VMOVDneon" && name2 == "VORRd") || - (name1 == "VMOVQ" && name2 == "VORRq")) { - // Inserting the opening curly brace for this case block. - --Indentation; --Indentation; - o.indent(Indentation) << "{\n"; - ++Indentation; ++Indentation; - - o.indent(Indentation) - << "field_t N = fieldFromInstruction(insn, 7, 1), " - << "M = fieldFromInstruction(insn, 5, 1);\n"; - o.indent(Indentation) - << "field_t Vn = fieldFromInstruction(insn, 16, 4), " - << "Vm = fieldFromInstruction(insn, 0, 4);\n"; - o.indent(Indentation) - << "return (N == M && Vn == Vm) ? " - << Opcodes[0] << " /* " << name1 << " */ : " - << Opcodes[1] << " /* " << name2 << " */ ;\n"; - - // Inserting the closing curly brace for this case block. - --Indentation; --Indentation; - o.indent(Indentation) << "}\n"; - ++Indentation; ++Indentation; - - return true; - } - if (name1 == "LDR" && name2 == "LDRcp") { - o.indent(Indentation) - << "return " << Opcodes[0] - << "; // Returning LDR for {LDR, LDRcp}\n"; - return true; - } - if (name1 == "tLDMIA" && name2 == "tLDMIA_UPD") { - // Inserting the opening curly brace for this case block. - --Indentation; --Indentation; - o.indent(Indentation) << "{\n"; - ++Indentation; ++Indentation; - - o.indent(Indentation) - << "unsigned Rn = fieldFromInstruction(insn, 8, 3), " - << "list = fieldFromInstruction(insn, 0, 8);\n"; - o.indent(Indentation) - << "return ((list >> Rn) & 1) == 0 ? " - << Opcodes[1] << " /* " << name2 << " */ : " - << Opcodes[0] << " /* " << name1 << " */ ;\n"; - - // Inserting the closing curly brace for this case block. - --Indentation; --Indentation; - o.indent(Indentation) << "}\n"; - ++Indentation; ++Indentation; - - return true; - } - if (sameStringExceptSuffix(name1, name2, "_UPD")) { - o.indent(Indentation) - << "return fieldFromInstruction(insn, 0, 4) == 15 ? " << Opcodes[0] - << " /* " << name1 << " */ : " << Opcodes[1] << "/* " << name2 - << " */ ; // Advanced SIMD addressing mode\n"; - return true; - } - - // Otherwise, it does not belong to the known conflict sets. - } - - // We don't know how to decode these instructions! Return 0 and dump the - // conflict set! - o.indent(Indentation) << "return 0;" << " // Conflict set: "; - for (int i = 0, N = Opcodes.size(); i < N; ++i) { - o << nameWithID(Opcodes[i]); - if (i < (N - 1)) - o << ", "; - else - o << '\n'; - } - - // Print out useful conflict information for postmortem analysis. - errs() << "Decoding Conflict:\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'; - } - - return true; -} - - -//////////////////////////////////////////// -// // -// ARMDEBackend // -// (Helper class for ARMDecoderEmitter) // -// // -//////////////////////////////////////////// - -class ARMDecoderEmitter::ARMDEBackend { -public: - ARMDEBackend(ARMDecoderEmitter &frontend, RecordKeeper &Records) : - NumberedInstructions(), - Opcodes(), - Frontend(frontend), - Target(Records), - FC(NULL) - { - if (Target.getName() == "ARM") - TargetName = TARGET_ARM; - else { - errs() << "Target name " << Target.getName() << " not recognized\n"; - assert(0 && "Unknown target"); - } - - // Populate the instructions for our TargetName. - populateInstructions(); - } - - ~ARMDEBackend() { - if (FC) { - delete FC; - FC = NULL; - } - } - - void getInstructionsByEnumValue(std::vector<const CodeGenInstruction*> - &NumberedInstructions) { - // We must emit the PHI opcode first... - std::string Namespace = Target.getInstNamespace(); - assert(!Namespace.empty() && "No instructions defined."); - - NumberedInstructions = Target.getInstructionsByEnumValue(); - } - - bool populateInstruction(const CodeGenInstruction &CGI, TARGET_NAME_t TN); - - void populateInstructions(); - - // Emits disassembler code for instruction decoding. This delegates to the - // FilterChooser instance to do the heavy lifting. - void emit(raw_ostream &o); - -protected: - std::vector<const CodeGenInstruction*> NumberedInstructions; - std::vector<unsigned> Opcodes; - // Special case for the ARM chip, which supports ARM and Thumb ISAs. - // Opcodes2 will be populated with the Thumb opcodes. - std::vector<unsigned> Opcodes2; - ARMDecoderEmitter &Frontend; - CodeGenTarget Target; - ARMFilterChooser *FC; - - TARGET_NAME_t TargetName; -}; - -bool ARMDecoderEmitter:: -ARMDEBackend::populateInstruction(const CodeGenInstruction &CGI, - TARGET_NAME_t TN) { - const Record &Def = *CGI.TheDef; - const StringRef Name = Def.getName(); - uint8_t Form = getByteField(Def, "Form"); - - BitsInit &Bits = getBitsField(Def, "Inst"); - - // If all the bit positions are not specified; do not decode this instruction. - // We are bound to fail! For proper disassembly, the well-known encoding bits - // of the instruction must be fully specified. - // - // This also removes pseudo instructions from considerations of disassembly, - // which is a better design and less fragile than the name matchings. - if (Bits.allInComplete()) return false; - - // Ignore "asm parser only" instructions. - if (Def.getValueAsBit("isAsmParserOnly")) - return false; - - if (TN == TARGET_ARM) { - if (Form == ARM_FORMAT_PSEUDO) - return false; - if (thumbInstruction(Form)) - return false; - - // Tail calls are other patterns that generate existing instructions. - if (Name == "TCRETURNdi" || Name == "TCRETURNdiND" || - Name == "TCRETURNri" || Name == "TCRETURNriND" || - Name == "TAILJMPd" || Name == "TAILJMPdt" || - Name == "TAILJMPdND" || Name == "TAILJMPdNDt" || - Name == "TAILJMPr" || Name == "TAILJMPrND" || - Name == "MOVr_TC") - return false; - - // Delegate ADR disassembly to the more generic ADDri/SUBri instructions. - if (Name == "ADR") - return false; - - // - // The following special cases are for conflict resolutions. - // - - // A8-598: VEXT - // Vector Extract extracts elements from the bottom end of the second - // operand vector and the top end of the first, concatenates them and - // places the result in the destination vector. The elements of the - // vectors are treated as being 8-bit bitfields. There is no distinction - // between data types. The size of the operation can be specified in - // assembler as vext.size. If the value is 16, 32, or 64, the syntax is - // a pseudo-instruction for a VEXT instruction specifying the equivalent - // number of bytes. - // - // Variants VEXTd16, VEXTd32, VEXTd8, and VEXTdf are reduced to VEXTd8; - // variants VEXTq16, VEXTq32, VEXTq8, and VEXTqf are reduced to VEXTq8. - if (Name == "VEXTd16" || Name == "VEXTd32" || Name == "VEXTdf" || - Name == "VEXTq16" || Name == "VEXTq32" || Name == "VEXTqf") - return false; - } else if (TN == TARGET_THUMB) { - if (!thumbInstruction(Form)) - return false; - - // A8.6.25 BX. Use the generic tBX_Rm, ignore tBX_RET and tBX_RET_vararg. - if (Name == "tBX_RET" || Name == "tBX_RET_vararg") - return false; - - // Ignore tADR, prefer tADDrPCi. - if (Name == "tADR") - return false; - - // Delegate t2ADR disassembly to the more generic t2ADDri12/t2SUBri12 - // instructions. - if (Name == "t2ADR") - return false; - - // Ignore tADDrSP, tADDspr, and tPICADD, prefer the generic tADDhirr. - // Ignore t2SUBrSPs, prefer the t2SUB[S]r[r|s]. - // Ignore t2ADDrSPs, prefer the t2ADD[S]r[r|s]. - if (Name == "tADDrSP" || Name == "tADDspr" || Name == "tPICADD" || - Name == "t2SUBrSPs" || Name == "t2ADDrSPs") - return false; - - // FIXME: Use ldr.n to work around a Darwin assembler bug. - // Introduce a workaround with tLDRpciDIS opcode. - if (Name == "tLDRpci") - return false; - - // Ignore t2LDRDpci, prefer the generic t2LDRDi8, t2LDRD_PRE, t2LDRD_POST. - if (Name == "t2LDRDpci") - return false; - - // Resolve conflicts: - // - // t2LDMIA_RET conflict with t2LDM (ditto) - // tMOVCCi conflicts with tMOVi8 - // tMOVCCr conflicts with tMOVgpr2gpr - // tLDRcp conflicts with tLDRspi - // t2MOVCCi16 conflicts with tMOVi16 - if (Name == "t2LDMIA_RET" || - Name == "tMOVCCi" || Name == "tMOVCCr" || - Name == "tLDRcp" || - Name == "t2MOVCCi16") - return false; - } - - DEBUG({ - // Dumps the instruction encoding format. - switch (TargetName) { - case TARGET_ARM: - case TARGET_THUMB: - errs() << Name << " " << stringForARMFormat((ARMFormat)Form); - break; - } - - errs() << " "; - - // Dumps the instruction encoding bits. - dumpBits(errs(), Bits); - - errs() << '\n'; - - // Dumps the list of operand info. - for (unsigned i = 0, e = CGI.Operands.size(); i != e; ++i) { - const CGIOperandList::OperandInfo &Info = CGI.Operands[i]; - const std::string &OperandName = Info.Name; - const Record &OperandDef = *Info.Rec; - - errs() << "\t" << OperandName << " (" << OperandDef.getName() << ")\n"; - } - }); - - return true; -} - -void ARMDecoderEmitter::ARMDEBackend::populateInstructions() { - getInstructionsByEnumValue(NumberedInstructions); - - unsigned numUIDs = NumberedInstructions.size(); - if (TargetName == TARGET_ARM) { - for (unsigned uid = 0; uid < numUIDs; uid++) { - // filter out intrinsics - if (!NumberedInstructions[uid]->TheDef->isSubClassOf("InstARM")) - continue; - - if (populateInstruction(*NumberedInstructions[uid], TargetName)) - Opcodes.push_back(uid); - } - - // Special handling for the ARM chip, which supports two modes of execution. - // This branch handles the Thumb opcodes. - for (unsigned uid = 0; uid < numUIDs; uid++) { - // filter out intrinsics - if (!NumberedInstructions[uid]->TheDef->isSubClassOf("InstARM") - && !NumberedInstructions[uid]->TheDef->isSubClassOf("InstThumb")) - continue; - - if (populateInstruction(*NumberedInstructions[uid], TARGET_THUMB)) - Opcodes2.push_back(uid); - } - - return; - } - - // For other targets. - for (unsigned uid = 0; uid < numUIDs; uid++) { - Record *R = NumberedInstructions[uid]->TheDef; - if (R->getValueAsString("Namespace") == "TargetOpcode") - continue; - - if (populateInstruction(*NumberedInstructions[uid], TargetName)) - Opcodes.push_back(uid); - } -} - -// Emits disassembler code for instruction decoding. This delegates to the -// FilterChooser instance to do the heavy lifting. -void ARMDecoderEmitter::ARMDEBackend::emit(raw_ostream &o) { - switch (TargetName) { - case TARGET_ARM: - Frontend.EmitSourceFileHeader("ARM/Thumb Decoders", o); - break; - default: - assert(0 && "Unreachable code!"); - } - - o << "#include \"llvm/Support/DataTypes.h\"\n"; - o << "#include <assert.h>\n"; - o << '\n'; - o << "namespace llvm {\n\n"; - - ARMFilterChooser::setTargetName(TargetName); - - switch (TargetName) { - case TARGET_ARM: { - // Emit common utility and ARM ISA decoder. - FC = new ARMFilterChooser(NumberedInstructions, Opcodes); - // Reset indentation level. - unsigned Indentation = 0; - FC->emitTop(o, Indentation); - delete FC; - - // Emit Thumb ISA decoder as well. - ARMFilterChooser::setTargetName(TARGET_THUMB); - FC = new ARMFilterChooser(NumberedInstructions, Opcodes2); - // Reset indentation level. - Indentation = 0; - FC->emitBot(o, Indentation); - break; - } - default: - assert(0 && "Unreachable code!"); - } - - o << "\n} // End llvm namespace \n"; -} - -///////////////////////// -// Backend interface // -///////////////////////// - -void ARMDecoderEmitter::initBackend() -{ - Backend = new ARMDEBackend(*this, Records); -} - -void ARMDecoderEmitter::run(raw_ostream &o) -{ - Backend->emit(o); -} - -void ARMDecoderEmitter::shutdownBackend() -{ - delete Backend; - Backend = NULL; -} diff --git a/utils/TableGen/ARMDecoderEmitter.h b/utils/TableGen/ARMDecoderEmitter.h deleted file mode 100644 index 486f899..0000000 --- a/utils/TableGen/ARMDecoderEmitter.h +++ /dev/null @@ -1,49 +0,0 @@ -//===------------ ARMDecoderEmitter.h - Decoder Generator -------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is part of the ARM Disassembler. -// It contains the tablegen backend declaration ARMDecoderEmitter. -// -//===----------------------------------------------------------------------===// - -#ifndef ARMDECODEREMITTER_H -#define ARMDECODEREMITTER_H - -#include "llvm/Support/DataTypes.h" -#include "llvm/TableGen/TableGenBackend.h" - -namespace llvm { - -class ARMDecoderEmitter : public TableGenBackend { - RecordKeeper &Records; -public: - ARMDecoderEmitter(RecordKeeper &R) : Records(R) { - initBackend(); - } - - ~ARMDecoderEmitter() { - shutdownBackend(); - } - - // run - Output the code emitter - void run(raw_ostream &o); - -private: - // Helper class for ARMDecoderEmitter. - class ARMDEBackend; - - ARMDEBackend *Backend; - - void initBackend(); - void shutdownBackend(); -}; - -} // end llvm namespace - -#endif diff --git a/utils/TableGen/AsmMatcherEmitter.cpp b/utils/TableGen/AsmMatcherEmitter.cpp index 8b86c23..39a3c25 100644 --- a/utils/TableGen/AsmMatcherEmitter.cpp +++ b/utils/TableGen/AsmMatcherEmitter.cpp @@ -99,6 +99,7 @@ #include "AsmMatcherEmitter.h" #include "CodeGenTarget.h" #include "StringMatcher.h" +#include "StringToOffsetTable.h" #include "llvm/ADT/OwningPtr.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/SmallPtrSet.h" @@ -107,6 +108,7 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/TableGen/Error.h" #include "llvm/TableGen/Record.h" #include <map> @@ -251,12 +253,7 @@ public: switch (Kind) { case Invalid: - assert(0 && "Invalid kind!"); - case Token: - // Tokens are comparable by value. - // - // FIXME: Compare by enum value. - return ValueName < RHS.ValueName; + llvm_unreachable("Invalid kind!"); default: // This class precedes the RHS if it is a proper subset of the RHS. @@ -287,7 +284,11 @@ struct MatchableInfo { /// The suboperand index within SrcOpName, or -1 for the entire operand. int SubOpIdx; - explicit AsmOperand(StringRef T) : Token(T), Class(0), SubOpIdx(-1) {} + /// Register record if this token is singleton register. + Record *SingletonReg; + + explicit AsmOperand(StringRef T) : Token(T), Class(0), SubOpIdx(-1), + SingletonReg(0) {} }; /// ResOperand - This represents a single operand in the result instruction @@ -366,6 +367,9 @@ struct MatchableInfo { } }; + /// AsmVariantID - Target's assembly syntax variant no. + int AsmVariantID; + /// TheDef - This is the definition of the instruction or InstAlias that this /// matchable came from. Record *const TheDef; @@ -406,24 +410,28 @@ struct MatchableInfo { std::string ConversionFnKind; MatchableInfo(const CodeGenInstruction &CGI) - : TheDef(CGI.TheDef), DefRec(&CGI), AsmString(CGI.AsmString) { + : AsmVariantID(0), TheDef(CGI.TheDef), DefRec(&CGI), + AsmString(CGI.AsmString) { } MatchableInfo(const CodeGenInstAlias *Alias) - : TheDef(Alias->TheDef), DefRec(Alias), AsmString(Alias->AsmString) { + : AsmVariantID(0), TheDef(Alias->TheDef), DefRec(Alias), + AsmString(Alias->AsmString) { } void Initialize(const AsmMatcherInfo &Info, - SmallPtrSet<Record*, 16> &SingletonRegisters); + SmallPtrSet<Record*, 16> &SingletonRegisters, + int AsmVariantNo, std::string &RegisterPrefix); /// Validate - Return true if this matchable is a valid thing to match against /// and perform a bunch of validity checking. bool Validate(StringRef CommentDelimiter, bool Hack) const; - /// getSingletonRegisterForAsmOperand - If the specified token is a singleton - /// register, return the Record for it, otherwise return null. - Record *getSingletonRegisterForAsmOperand(unsigned i, - const AsmMatcherInfo &Info) const; + /// extractSingletonRegisterForAsmOperand - Extract singleton register, + /// if present, from specified token. + void + extractSingletonRegisterForAsmOperand(unsigned i, const AsmMatcherInfo &Info, + std::string &RegisterPrefix); /// FindAsmOperand - Find the AsmOperand with the specified name and /// suboperand index. @@ -557,9 +565,6 @@ public: /// Target - The target information. CodeGenTarget &Target; - /// The AsmParser "RegisterPrefix" value. - std::string RegisterPrefix; - /// The classes which are needed for matching. std::vector<ClassInfo*> Classes; @@ -591,7 +596,8 @@ private: /// getOperandClass - Lookup or create the class for the given operand. ClassInfo *getOperandClass(const CGIOperandList::OperandInfo &OI, - int SubOpIdx = -1); + int SubOpIdx); + ClassInfo *getOperandClass(Record *Rec, int SubOpIdx); /// BuildRegisterClasses - Build the ClassInfo* instances for register /// classes. @@ -645,9 +651,11 @@ void MatchableInfo::dump() { } void MatchableInfo::Initialize(const AsmMatcherInfo &Info, - SmallPtrSet<Record*, 16> &SingletonRegisters) { - // TODO: Eventually support asmparser for Variant != 0. - AsmString = CodeGenInstruction::FlattenAsmStringVariants(AsmString, 0); + SmallPtrSet<Record*, 16> &SingletonRegisters, + int AsmVariantNo, std::string &RegisterPrefix) { + AsmVariantID = AsmVariantNo; + AsmString = + CodeGenInstruction::FlattenAsmStringVariants(AsmString, AsmVariantNo); TokenizeAsmString(Info); @@ -660,7 +668,8 @@ void MatchableInfo::Initialize(const AsmMatcherInfo &Info, // Collect singleton registers, if used. for (unsigned i = 0, e = AsmOperands.size(); i != e; ++i) { - if (Record *Reg = getSingletonRegisterForAsmOperand(i, Info)) + extractSingletonRegisterForAsmOperand(i, Info, RegisterPrefix); + if (Record *Reg = AsmOperands[i].SingletonReg) SingletonRegisters.insert(Reg); } } @@ -736,9 +745,12 @@ void MatchableInfo::TokenizeAsmString(const AsmMatcherInfo &Info) { // The first token of the instruction is the mnemonic, which must be a // simple string, not a $foo variable or a singleton register. - assert(!AsmOperands.empty() && "Instruction has no tokens?"); + if (AsmOperands.empty()) + throw TGError(TheDef->getLoc(), + "Instruction '" + TheDef->getName() + "' has no tokens"); Mnemonic = AsmOperands[0].Token; - if (Mnemonic[0] == '$' || getSingletonRegisterForAsmOperand(0, Info)) + // FIXME : Check and raise an error if it is a register. + if (Mnemonic[0] == '$') throw TGError(TheDef->getLoc(), "Invalid instruction mnemonic '" + Mnemonic.str() + "'!"); @@ -801,28 +813,30 @@ bool MatchableInfo::Validate(StringRef CommentDelimiter, bool Hack) const { return true; } -/// getSingletonRegisterForAsmOperand - If the specified token is a singleton -/// register, return the register name, otherwise return a null StringRef. -Record *MatchableInfo:: -getSingletonRegisterForAsmOperand(unsigned i, const AsmMatcherInfo &Info) const{ - StringRef Tok = AsmOperands[i].Token; - if (!Tok.startswith(Info.RegisterPrefix)) - return 0; +/// extractSingletonRegisterForAsmOperand - Extract singleton register, +/// if present, from specified token. +void MatchableInfo:: +extractSingletonRegisterForAsmOperand(unsigned OperandNo, + const AsmMatcherInfo &Info, + std::string &RegisterPrefix) { + StringRef Tok = AsmOperands[OperandNo].Token; + if (RegisterPrefix.empty()) { + std::string LoweredTok = Tok.lower(); + if (const CodeGenRegister *Reg = Info.Target.getRegisterByName(LoweredTok)) + AsmOperands[OperandNo].SingletonReg = Reg->TheDef; + return; + } + + if (!Tok.startswith(RegisterPrefix)) + return; - StringRef RegName = Tok.substr(Info.RegisterPrefix.size()); + StringRef RegName = Tok.substr(RegisterPrefix.size()); if (const CodeGenRegister *Reg = Info.Target.getRegisterByName(RegName)) - return Reg->TheDef; + AsmOperands[OperandNo].SingletonReg = Reg->TheDef; // If there is no register prefix (i.e. "%" in "%eax"), then this may // be some random non-register token, just ignore it. - if (Info.RegisterPrefix.empty()) - return 0; - - // Otherwise, we have something invalid prefixed with the register prefix, - // such as %foo. - std::string Err = "unable to find register for '" + RegName.str() + - "' (which matches register prefix)"; - throw TGError(TheDef->getLoc(), Err); + return; } static std::string getEnumNameForToken(StringRef Str) { @@ -870,7 +884,11 @@ AsmMatcherInfo::getOperandClass(const CGIOperandList::OperandInfo &OI, Record *Rec = OI.Rec; if (SubOpIdx != -1) Rec = dynamic_cast<DefInit*>(OI.MIOperandInfo->getArg(SubOpIdx))->getDef(); + return getOperandClass(Rec, SubOpIdx); +} +ClassInfo * +AsmMatcherInfo::getOperandClass(Record *Rec, int SubOpIdx) { if (Rec->isSubClassOf("RegisterOperand")) { // RegisterOperand may have an associated ParserMatchClass. If it does, // use it, else just fall back to the underlying register class. @@ -1102,8 +1120,7 @@ void AsmMatcherInfo::BuildOperandClasses() { AsmMatcherInfo::AsmMatcherInfo(Record *asmParser, CodeGenTarget &target, RecordKeeper &records) - : Records(records), AsmParser(asmParser), Target(target), - RegisterPrefix(AsmParser->getValueAsString("RegisterPrefix")) { + : Records(records), AsmParser(asmParser), Target(target) { } /// BuildOperandMatchInfo - Build the necessary information to handle user @@ -1158,86 +1175,92 @@ void AsmMatcherInfo::BuildInfo() { assert(FeatureNo < 32 && "Too many subtarget features!"); } - std::string CommentDelimiter = AsmParser->getValueAsString("CommentDelimiter"); - // Parse the instructions; we need to do this first so that we can gather the // singleton register classes. SmallPtrSet<Record*, 16> SingletonRegisters; - for (CodeGenTarget::inst_iterator I = Target.inst_begin(), - E = Target.inst_end(); I != E; ++I) { - const CodeGenInstruction &CGI = **I; - - // If the tblgen -match-prefix option is specified (for tblgen hackers), - // filter the set of instructions we consider. - if (!StringRef(CGI.TheDef->getName()).startswith(MatchPrefix)) - continue; + unsigned VariantCount = Target.getAsmParserVariantCount(); + for (unsigned VC = 0; VC != VariantCount; ++VC) { + Record *AsmVariant = Target.getAsmParserVariant(VC); + std::string CommentDelimiter = AsmVariant->getValueAsString("CommentDelimiter"); + std::string RegisterPrefix = AsmVariant->getValueAsString("RegisterPrefix"); + int AsmVariantNo = AsmVariant->getValueAsInt("Variant"); + + for (CodeGenTarget::inst_iterator I = Target.inst_begin(), + E = Target.inst_end(); I != E; ++I) { + const CodeGenInstruction &CGI = **I; + + // If the tblgen -match-prefix option is specified (for tblgen hackers), + // filter the set of instructions we consider. + if (!StringRef(CGI.TheDef->getName()).startswith(MatchPrefix)) + continue; - // Ignore "codegen only" instructions. - if (CGI.TheDef->getValueAsBit("isCodeGenOnly")) - continue; + // Ignore "codegen only" instructions. + if (CGI.TheDef->getValueAsBit("isCodeGenOnly")) + continue; - // Validate the operand list to ensure we can handle this instruction. - for (unsigned i = 0, e = CGI.Operands.size(); i != e; ++i) { - const CGIOperandList::OperandInfo &OI = CGI.Operands[i]; - - // Validate tied operands. - if (OI.getTiedRegister() != -1) { - // If we have a tied operand that consists of multiple MCOperands, - // reject it. We reject aliases and ignore instructions for now. - if (OI.MINumOperands != 1) { - // FIXME: Should reject these. The ARM backend hits this with $lane - // in a bunch of instructions. It is unclear what the right answer is. - DEBUG({ - errs() << "warning: '" << CGI.TheDef->getName() << "': " - << "ignoring instruction with multi-operand tied operand '" - << OI.Name << "'\n"; - }); - continue; + // Validate the operand list to ensure we can handle this instruction. + for (unsigned i = 0, e = CGI.Operands.size(); i != e; ++i) { + const CGIOperandList::OperandInfo &OI = CGI.Operands[i]; + + // Validate tied operands. + if (OI.getTiedRegister() != -1) { + // If we have a tied operand that consists of multiple MCOperands, + // reject it. We reject aliases and ignore instructions for now. + if (OI.MINumOperands != 1) { + // FIXME: Should reject these. The ARM backend hits this with $lane + // in a bunch of instructions. It is unclear what the right answer is. + DEBUG({ + errs() << "warning: '" << CGI.TheDef->getName() << "': " + << "ignoring instruction with multi-operand tied operand '" + << OI.Name << "'\n"; + }); + continue; + } } } - } - OwningPtr<MatchableInfo> II(new MatchableInfo(CGI)); + OwningPtr<MatchableInfo> II(new MatchableInfo(CGI)); - II->Initialize(*this, SingletonRegisters); + II->Initialize(*this, SingletonRegisters, AsmVariantNo, RegisterPrefix); - // Ignore instructions which shouldn't be matched and diagnose invalid - // instruction definitions with an error. - if (!II->Validate(CommentDelimiter, true)) - continue; + // Ignore instructions which shouldn't be matched and diagnose invalid + // instruction definitions with an error. + if (!II->Validate(CommentDelimiter, true)) + continue; - // Ignore "Int_*" and "*_Int" instructions, which are internal aliases. - // - // FIXME: This is a total hack. - if (StringRef(II->TheDef->getName()).startswith("Int_") || - StringRef(II->TheDef->getName()).endswith("_Int")) - continue; + // Ignore "Int_*" and "*_Int" instructions, which are internal aliases. + // + // FIXME: This is a total hack. + if (StringRef(II->TheDef->getName()).startswith("Int_") || + StringRef(II->TheDef->getName()).endswith("_Int")) + continue; - Matchables.push_back(II.take()); - } + Matchables.push_back(II.take()); + } - // Parse all of the InstAlias definitions and stick them in the list of - // matchables. - std::vector<Record*> AllInstAliases = - Records.getAllDerivedDefinitions("InstAlias"); - for (unsigned i = 0, e = AllInstAliases.size(); i != e; ++i) { - CodeGenInstAlias *Alias = new CodeGenInstAlias(AllInstAliases[i], Target); - - // If the tblgen -match-prefix option is specified (for tblgen hackers), - // filter the set of instruction aliases we consider, based on the target - // instruction. - if (!StringRef(Alias->ResultInst->TheDef->getName()).startswith( - MatchPrefix)) - continue; + // Parse all of the InstAlias definitions and stick them in the list of + // matchables. + std::vector<Record*> AllInstAliases = + Records.getAllDerivedDefinitions("InstAlias"); + for (unsigned i = 0, e = AllInstAliases.size(); i != e; ++i) { + CodeGenInstAlias *Alias = new CodeGenInstAlias(AllInstAliases[i], Target); + + // If the tblgen -match-prefix option is specified (for tblgen hackers), + // filter the set of instruction aliases we consider, based on the target + // instruction. + if (!StringRef(Alias->ResultInst->TheDef->getName()).startswith( + MatchPrefix)) + continue; - OwningPtr<MatchableInfo> II(new MatchableInfo(Alias)); + OwningPtr<MatchableInfo> II(new MatchableInfo(Alias)); - II->Initialize(*this, SingletonRegisters); + II->Initialize(*this, SingletonRegisters, AsmVariantNo, RegisterPrefix); - // Validate the alias definitions. - II->Validate(CommentDelimiter, false); + // Validate the alias definitions. + II->Validate(CommentDelimiter, false); - Matchables.push_back(II.take()); + Matchables.push_back(II.take()); + } } // Build info for the register classes. @@ -1260,7 +1283,7 @@ void AsmMatcherInfo::BuildInfo() { StringRef Token = Op.Token; // Check for singleton registers. - if (Record *RegRecord = II->getSingletonRegisterForAsmOperand(i, *this)) { + if (Record *RegRecord = II->AsmOperands[i].SingletonReg) { Op.Class = RegisterClasses[RegRecord]; assert(Op.Class && Op.Class->Registers.size() == 1 && "Unexpected class for singleton register"); @@ -1297,6 +1320,17 @@ void AsmMatcherInfo::BuildInfo() { II->BuildAliasResultOperands(); } + // Process token alias definitions and set up the associated superclass + // information. + std::vector<Record*> AllTokenAliases = + Records.getAllDerivedDefinitions("TokenAlias"); + for (unsigned i = 0, e = AllTokenAliases.size(); i != e; ++i) { + Record *Rec = AllTokenAliases[i]; + ClassInfo *FromClass = getTokenClass(Rec->getValueAsString("FromToken")); + ClassInfo *ToClass = getTokenClass(Rec->getValueAsString("ToToken")); + FromClass->SuperClasses.push_back(ToClass); + } + // Reorder classes so that classes precede super classes. std::sort(Classes.begin(), Classes.end(), less_ptr<ClassInfo>()); } @@ -1375,9 +1409,11 @@ void AsmMatcherInfo::BuildAliasOperandReference(MatchableInfo *II, CGA.ResultOperands[i].getName() == OperandName) { // It's safe to go with the first one we find, because CodeGenInstAlias // validates that all operands with the same name have the same record. - unsigned ResultIdx = CGA.ResultInstOperandIndex[i].first; Op.SubOpIdx = CGA.ResultInstOperandIndex[i].second; - Op.Class = getOperandClass(CGA.ResultInst->Operands[ResultIdx], + // Use the match class from the Alias definition, not the + // destination instruction, as we may have an immediate that's + // being munged by the match class. + Op.Class = getOperandClass(CGA.ResultOperands[i].getRecord(), Op.SubOpIdx); Op.SrcOpName = OperandName; return; @@ -1453,7 +1489,6 @@ void MatchableInfo::BuildAliasResultOperands() { // Find out what operand from the asmparser that this MCInst operand // comes from. switch (CGA.ResultOperands[AliasOpNo].Kind) { - default: assert(0 && "unexpected InstAlias operand kind"); case CodeGenInstAlias::ResultOperand::K_Record: { StringRef Name = CGA.ResultOperands[AliasOpNo].getName(); int SrcOperand = FindAsmOperand(Name, SubIdx); @@ -1656,7 +1691,7 @@ static void EmitMatchClassEnumeration(CodeGenTarget &Target, /// EmitValidateOperandClass - Emit the function to validate an operand class. static void EmitValidateOperandClass(AsmMatcherInfo &Info, raw_ostream &OS) { - OS << "static bool ValidateOperandClass(MCParsedAsmOperand *GOp, " + OS << "static bool validateOperandClass(MCParsedAsmOperand *GOp, " << "MatchClassKind Kind) {\n"; OS << " " << Info.Target.getName() << "Operand &Operand = *(" << Info.Target.getName() << "Operand*)GOp;\n"; @@ -1667,7 +1702,8 @@ static void EmitValidateOperandClass(AsmMatcherInfo &Info, // Check for Token operands first. OS << " if (Operand.isToken())\n"; - OS << " return MatchTokenString(Operand.getToken()) == Kind;\n\n"; + OS << " return isSubclass(matchTokenString(Operand.getToken()), Kind);" + << "\n\n"; // Check for register operands, including sub-classes. OS << " if (Operand.isReg()) {\n"; @@ -1681,7 +1717,7 @@ static void EmitValidateOperandClass(AsmMatcherInfo &Info, << it->first->getName() << ": OpKind = " << it->second->Name << "; break;\n"; OS << " }\n"; - OS << " return IsSubclass(OpKind, Kind);\n"; + OS << " return isSubclass(OpKind, Kind);\n"; OS << " }\n\n"; // Check the user classes. We don't care what order since we're only @@ -1708,8 +1744,8 @@ static void EmitValidateOperandClass(AsmMatcherInfo &Info, static void EmitIsSubclass(CodeGenTarget &Target, std::vector<ClassInfo*> &Infos, raw_ostream &OS) { - OS << "/// IsSubclass - Compute whether \\arg A is a subclass of \\arg B.\n"; - OS << "static bool IsSubclass(MatchClassKind A, MatchClassKind B) {\n"; + OS << "/// isSubclass - Compute whether \\arg A is a subclass of \\arg B.\n"; + OS << "static bool isSubclass(MatchClassKind A, MatchClassKind B) {\n"; OS << " if (A == B)\n"; OS << " return true;\n\n"; @@ -1720,32 +1756,30 @@ static void EmitIsSubclass(CodeGenTarget &Target, ie = Infos.end(); it != ie; ++it) { ClassInfo &A = **it; - if (A.Kind != ClassInfo::Token) { - std::vector<StringRef> SuperClasses; - for (std::vector<ClassInfo*>::iterator it = Infos.begin(), - ie = Infos.end(); it != ie; ++it) { - ClassInfo &B = **it; - - if (&A != &B && A.isSubsetOf(B)) - SuperClasses.push_back(B.Name); - } + std::vector<StringRef> SuperClasses; + for (std::vector<ClassInfo*>::iterator it = Infos.begin(), + ie = Infos.end(); it != ie; ++it) { + ClassInfo &B = **it; - if (SuperClasses.empty()) - continue; + if (&A != &B && A.isSubsetOf(B)) + SuperClasses.push_back(B.Name); + } - OS << "\n case " << A.Name << ":\n"; + if (SuperClasses.empty()) + continue; - if (SuperClasses.size() == 1) { - OS << " return B == " << SuperClasses.back() << ";\n"; - continue; - } + OS << "\n case " << A.Name << ":\n"; - OS << " switch (B) {\n"; - OS << " default: return false;\n"; - for (unsigned i = 0, e = SuperClasses.size(); i != e; ++i) - OS << " case " << SuperClasses[i] << ": return true;\n"; - OS << " }\n"; + if (SuperClasses.size() == 1) { + OS << " return B == " << SuperClasses.back() << ";\n"; + continue; } + + OS << " switch (B) {\n"; + OS << " default: return false;\n"; + for (unsigned i = 0, e = SuperClasses.size(); i != e; ++i) + OS << " case " << SuperClasses[i] << ": return true;\n"; + OS << " }\n"; } OS << " }\n"; OS << "}\n\n"; @@ -1767,7 +1801,7 @@ static void EmitMatchTokenString(CodeGenTarget &Target, "return " + CI.Name + ";")); } - OS << "static MatchClassKind MatchTokenString(StringRef Name) {\n"; + OS << "static MatchClassKind matchTokenString(StringRef Name) {\n"; StringMatcher("Name", Matches, OS).Emit(); @@ -1905,7 +1939,7 @@ static bool EmitMnemonicAliases(raw_ostream &OS, const AsmMatcherInfo &Info) { Info.getRecords().getAllDerivedDefinitions("MnemonicAlias"); if (Aliases.empty()) return false; - OS << "static void ApplyMnemonicAliases(StringRef &Mnemonic, " + OS << "static void applyMnemonicAliases(StringRef &Mnemonic, " "unsigned Features) {\n"; // Keep track of all the aliases from a mnemonic. Use an std::map so that the @@ -1975,45 +2009,62 @@ static bool EmitMnemonicAliases(raw_ostream &OS, const AsmMatcherInfo &Info) { return true; } +static const char *getMinimalTypeForRange(uint64_t Range) { + assert(Range < 0xFFFFFFFFULL && "Enum too large"); + if (Range > 0xFFFF) + return "uint32_t"; + if (Range > 0xFF) + return "uint16_t"; + return "uint8_t"; +} + static void EmitCustomOperandParsing(raw_ostream &OS, CodeGenTarget &Target, const AsmMatcherInfo &Info, StringRef ClassName) { // Emit the static custom operand parsing table; OS << "namespace {\n"; OS << " struct OperandMatchEntry {\n"; - OS << " const char *Mnemonic;\n"; - OS << " unsigned OperandMask;\n"; - OS << " MatchClassKind Class;\n"; - OS << " unsigned RequiredFeatures;\n"; + OS << " static const char *const MnemonicTable;\n"; + OS << " uint32_t OperandMask;\n"; + OS << " uint32_t Mnemonic;\n"; + OS << " " << getMinimalTypeForRange(1ULL << Info.SubtargetFeatures.size()) + << " RequiredFeatures;\n"; + OS << " " << getMinimalTypeForRange(Info.Classes.size()) + << " Class;\n\n"; + OS << " StringRef getMnemonic() const {\n"; + OS << " return StringRef(MnemonicTable + Mnemonic + 1,\n"; + OS << " MnemonicTable[Mnemonic]);\n"; + OS << " }\n"; OS << " };\n\n"; OS << " // Predicate for searching for an opcode.\n"; OS << " struct LessOpcodeOperand {\n"; OS << " bool operator()(const OperandMatchEntry &LHS, StringRef RHS) {\n"; - OS << " return StringRef(LHS.Mnemonic) < RHS;\n"; + OS << " return LHS.getMnemonic() < RHS;\n"; OS << " }\n"; OS << " bool operator()(StringRef LHS, const OperandMatchEntry &RHS) {\n"; - OS << " return LHS < StringRef(RHS.Mnemonic);\n"; + OS << " return LHS < RHS.getMnemonic();\n"; OS << " }\n"; OS << " bool operator()(const OperandMatchEntry &LHS,"; OS << " const OperandMatchEntry &RHS) {\n"; - OS << " return StringRef(LHS.Mnemonic) < StringRef(RHS.Mnemonic);\n"; + OS << " return LHS.getMnemonic() < RHS.getMnemonic();\n"; OS << " }\n"; OS << " };\n"; OS << "} // end anonymous namespace.\n\n"; + StringToOffsetTable StringTable; + OS << "static const OperandMatchEntry OperandMatchTable[" << Info.OperandMatchInfo.size() << "] = {\n"; - OS << " /* Mnemonic, Operand List Mask, Operand Class, Features */\n"; + OS << " /* Operand List Mask, Mnemonic, Operand Class, Features */\n"; for (std::vector<OperandMatchEntry>::const_iterator it = Info.OperandMatchInfo.begin(), ie = Info.OperandMatchInfo.end(); it != ie; ++it) { const OperandMatchEntry &OMI = *it; const MatchableInfo &II = *OMI.MI; - OS << " { \"" << II.Mnemonic << "\"" - << ", " << OMI.OperandMask; + OS << " { " << OMI.OperandMask; OS << " /* "; bool printComma = false; @@ -2026,8 +2077,10 @@ static void EmitCustomOperandParsing(raw_ostream &OS, CodeGenTarget &Target, } OS << " */"; - OS << ", " << OMI.CI->Name - << ", "; + // Store a pascal-style length byte in the mnemonic. + std::string LenMnemonic = char(II.Mnemonic.size()) + II.Mnemonic.str(); + OS << ", " << StringTable.GetOrAddStringOffset(LenMnemonic, false) + << " /* " << II.Mnemonic << " */, "; // Write the required features mask. if (!II.RequiredFeatures.empty()) { @@ -2037,15 +2090,22 @@ static void EmitCustomOperandParsing(raw_ostream &OS, CodeGenTarget &Target, } } else OS << "0"; + + OS << ", " << OMI.CI->Name; + OS << " },\n"; } OS << "};\n\n"; + OS << "const char *const OperandMatchEntry::MnemonicTable =\n"; + StringTable.EmitString(OS); + OS << ";\n\n"; + // Emit the operand class switch to call the correct custom parser for // the found operand class. OS << Target.getName() << ClassName << "::OperandMatchResultTy " << Target.getName() << ClassName << "::\n" - << "TryCustomParseOperand(SmallVectorImpl<MCParsedAsmOperand*>" + << "tryCustomParseOperand(SmallVectorImpl<MCParsedAsmOperand*>" << " &Operands,\n unsigned MCK) {\n\n" << " switch(MCK) {\n"; @@ -2094,7 +2154,7 @@ static void EmitCustomOperandParsing(raw_ostream &OS, CodeGenTarget &Target, << " *ie = MnemonicRange.second; it != ie; ++it) {\n"; OS << " // equal_range guarantees that instruction mnemonic matches.\n"; - OS << " assert(Mnemonic == it->Mnemonic);\n\n"; + OS << " assert(Mnemonic == it->getMnemonic());\n\n"; // Emit check that the required features are available. OS << " // check if the available features match\n"; @@ -2111,7 +2171,7 @@ static void EmitCustomOperandParsing(raw_ostream &OS, CodeGenTarget &Target, // Emit call to the custom parser method OS << " // call custom parse method to handle the operand\n"; OS << " OperandMatchResultTy Result = "; - OS << "TryCustomParseOperand(Operands, it->Class);\n"; + OS << "tryCustomParseOperand(Operands, it->Class);\n"; OS << " if (Result != MatchOperand_NoMatch)\n"; OS << " return Result;\n"; OS << " }\n\n"; @@ -2186,7 +2246,7 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { OS << " bool MnemonicIsValid(StringRef Mnemonic);\n"; OS << " unsigned MatchInstructionImpl(\n"; OS << " const SmallVectorImpl<MCParsedAsmOperand*> &Operands,\n"; - OS << " MCInst &Inst, unsigned &ErrorInfo);\n"; + OS << " MCInst &Inst, unsigned &ErrorInfo, unsigned VariantID = 0);\n"; if (Info.OperandMatchInfo.size()) { OS << "\n enum OperandMatchResultTy {\n"; @@ -2198,7 +2258,7 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { OS << " SmallVectorImpl<MCParsedAsmOperand*> &Operands,\n"; OS << " StringRef Mnemonic);\n"; - OS << " OperandMatchResultTy TryCustomParseOperand(\n"; + OS << " OperandMatchResultTy tryCustomParseOperand(\n"; OS << " SmallVectorImpl<MCParsedAsmOperand*> &Operands,\n"; OS << " unsigned MCK);\n\n"; } @@ -2260,28 +2320,39 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { // following the mnemonic. OS << "namespace {\n"; OS << " struct MatchEntry {\n"; - OS << " unsigned Opcode;\n"; - OS << " const char *Mnemonic;\n"; - OS << " ConversionKind ConvertFn;\n"; - OS << " MatchClassKind Classes[" << MaxNumOperands << "];\n"; - OS << " unsigned RequiredFeatures;\n"; + OS << " static const char *const MnemonicTable;\n"; + OS << " uint32_t Mnemonic;\n"; + OS << " uint16_t Opcode;\n"; + OS << " " << getMinimalTypeForRange(Info.Matchables.size()) + << " ConvertFn;\n"; + OS << " " << getMinimalTypeForRange(1ULL << Info.SubtargetFeatures.size()) + << " RequiredFeatures;\n"; + OS << " " << getMinimalTypeForRange(Info.Classes.size()) + << " Classes[" << MaxNumOperands << "];\n"; + OS << " uint8_t AsmVariantID;\n\n"; + OS << " StringRef getMnemonic() const {\n"; + OS << " return StringRef(MnemonicTable + Mnemonic + 1,\n"; + OS << " MnemonicTable[Mnemonic]);\n"; + OS << " }\n"; OS << " };\n\n"; OS << " // Predicate for searching for an opcode.\n"; OS << " struct LessOpcode {\n"; OS << " bool operator()(const MatchEntry &LHS, StringRef RHS) {\n"; - OS << " return StringRef(LHS.Mnemonic) < RHS;\n"; + OS << " return LHS.getMnemonic() < RHS;\n"; OS << " }\n"; OS << " bool operator()(StringRef LHS, const MatchEntry &RHS) {\n"; - OS << " return LHS < StringRef(RHS.Mnemonic);\n"; + OS << " return LHS < RHS.getMnemonic();\n"; OS << " }\n"; OS << " bool operator()(const MatchEntry &LHS, const MatchEntry &RHS) {\n"; - OS << " return StringRef(LHS.Mnemonic) < StringRef(RHS.Mnemonic);\n"; + OS << " return LHS.getMnemonic() < RHS.getMnemonic();\n"; OS << " }\n"; OS << " };\n"; OS << "} // end anonymous namespace.\n\n"; + StringToOffsetTable StringTable; + OS << "static const MatchEntry MatchTable[" << Info.Matchables.size() << "] = {\n"; @@ -2290,16 +2361,13 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { it != ie; ++it) { MatchableInfo &II = **it; - OS << " { " << Target.getName() << "::" - << II.getResultInst()->TheDef->getName() << ", \"" << II.Mnemonic << "\"" - << ", " << II.ConversionFnKind << ", { "; - for (unsigned i = 0, e = II.AsmOperands.size(); i != e; ++i) { - MatchableInfo::AsmOperand &Op = II.AsmOperands[i]; - - if (i) OS << ", "; - OS << Op.Class->Name; - } - OS << " }, "; + // Store a pascal-style length byte in the mnemonic. + std::string LenMnemonic = char(II.Mnemonic.size()) + II.Mnemonic.str(); + OS << " { " << StringTable.GetOrAddStringOffset(LenMnemonic, false) + << " /* " << II.Mnemonic << " */, " + << Target.getName() << "::" + << II.getResultInst()->TheDef->getName() << ", " + << II.ConversionFnKind << ", "; // Write the required features mask. if (!II.RequiredFeatures.empty()) { @@ -2310,11 +2378,23 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { } else OS << "0"; + OS << ", { "; + for (unsigned i = 0, e = II.AsmOperands.size(); i != e; ++i) { + MatchableInfo::AsmOperand &Op = II.AsmOperands[i]; + + if (i) OS << ", "; + OS << Op.Class->Name; + } + OS << " }, " << II.AsmVariantID; OS << "},\n"; } OS << "};\n\n"; + OS << "const char *const MatchEntry::MnemonicTable =\n"; + StringTable.EmitString(OS); + OS << ";\n\n"; + // A method to determine if a mnemonic is in the list. OS << "bool " << Target.getName() << ClassName << "::\n" << "MnemonicIsValid(StringRef Mnemonic) {\n"; @@ -2330,7 +2410,8 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { << Target.getName() << ClassName << "::\n" << "MatchInstructionImpl(const SmallVectorImpl<MCParsedAsmOperand*>" << " &Operands,\n"; - OS << " MCInst &Inst, unsigned &ErrorInfo) {\n"; + OS << " MCInst &Inst, unsigned &ErrorInfo,\n"; + OS << " unsigned VariantID) {\n"; // Emit code to get the available features. OS << " // Get the current feature set.\n"; @@ -2342,7 +2423,9 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { if (HasMnemonicAliases) { OS << " // Process all MnemonicAliases to remap the mnemonic.\n"; - OS << " ApplyMnemonicAliases(Mnemonic, AvailableFeatures);\n\n"; + OS << " // FIXME : Add an entry in AsmParserVariant to check this.\n"; + OS << " if (!VariantID)\n"; + OS << " applyMnemonicAliases(Mnemonic, AvailableFeatures);\n\n"; } // Emit code to compute the class list for this operand vector. @@ -2375,16 +2458,18 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { OS << " it != ie; ++it) {\n"; OS << " // equal_range guarantees that instruction mnemonic matches.\n"; - OS << " assert(Mnemonic == it->Mnemonic);\n"; + OS << " assert(Mnemonic == it->getMnemonic());\n"; // Emit check that the subclasses match. + OS << " if (VariantID != it->AsmVariantID) continue;\n"; OS << " bool OperandsValid = true;\n"; OS << " for (unsigned i = 0; i != " << MaxNumOperands << "; ++i) {\n"; OS << " if (i + 1 >= Operands.size()) {\n"; OS << " OperandsValid = (it->Classes[i] == " <<"InvalidMatchClass);\n"; OS << " break;\n"; OS << " }\n"; - OS << " if (ValidateOperandClass(Operands[i+1], it->Classes[i]))\n"; + OS << " if (validateOperandClass(Operands[i+1], " + "(MatchClassKind)it->Classes[i]))\n"; OS << " continue;\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"; diff --git a/utils/TableGen/AsmWriterEmitter.cpp b/utils/TableGen/AsmWriterEmitter.cpp index 3123e11..e0b0aac 100644 --- a/utils/TableGen/AsmWriterEmitter.cpp +++ b/utils/TableGen/AsmWriterEmitter.cpp @@ -16,6 +16,7 @@ #include "AsmWriterInst.h" #include "CodeGenTarget.h" #include "StringToOffsetTable.h" +#include "SequenceToOffsetTable.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/MathExtras.h" @@ -277,12 +278,27 @@ void AsmWriterEmitter::EmitPrintInstruction(raw_ostream &O) { CGIAWIMap.insert(std::make_pair(Instructions[i].CGI, &Instructions[i])); // Build an aggregate string, and build a table of offsets into it. - StringToOffsetTable StringTable; + SequenceToOffsetTable<std::string> StringTable; /// 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<unsigned> OpcodeInfo; + // Add all strings to the string table upfront so it can generate an optimized + // representation. + for (unsigned i = 0, e = NumberedInstructions.size(); i != e; ++i) { + AsmWriterInst *AWI = CGIAWIMap[NumberedInstructions[i]]; + if (AWI != 0 && + AWI->Operands[0].OperandType == AsmWriterOperand::isLiteralTextOperand && + !AWI->Operands[0].Str.empty()) { + std::string Str = AWI->Operands[0].Str; + UnescapeString(Str); + StringTable.add(Str); + } + } + + StringTable.layout(); + unsigned MaxStringIdx = 0; for (unsigned i = 0, e = NumberedInstructions.size(); i != e; ++i) { AsmWriterInst *AWI = CGIAWIMap[NumberedInstructions[i]]; @@ -294,11 +310,11 @@ void AsmWriterEmitter::EmitPrintInstruction(raw_ostream &O) { AsmWriterOperand::isLiteralTextOperand || AWI->Operands[0].Str.empty()) { // Something handled by the asmwriter printer, but with no leading string. - Idx = StringTable.GetOrAddStringOffset(""); + Idx = StringTable.get(""); } else { std::string Str = AWI->Operands[0].Str; UnescapeString(Str); - Idx = StringTable.GetOrAddStringOffset(Str); + Idx = StringTable.get(Str); MaxStringIdx = std::max(MaxStringIdx, Idx); // Nuke the string from the operand list. It is now handled! @@ -373,9 +389,9 @@ void AsmWriterEmitter::EmitPrintInstruction(raw_ostream &O) { O << " };\n\n"; // Emit the string itself. - O << " const char *AsmStrs = \n"; - StringTable.EmitString(O); - O << ";\n\n"; + O << " const char AsmStrs[] = {\n"; + StringTable.emit(O, printChar); + O << " };\n\n"; O << " O << \"\\t\";\n\n"; @@ -461,13 +477,13 @@ void AsmWriterEmitter::EmitPrintInstruction(raw_ostream &O) { static void emitRegisterNameString(raw_ostream &O, StringRef AltName, - const std::vector<CodeGenRegister*> &Registers) { - StringToOffsetTable StringTable; - O << " static const unsigned RegAsmOffset" << AltName << "[] = {\n "; + const std::vector<CodeGenRegister*> &Registers) { + SequenceToOffsetTable<std::string> StringTable; + SmallVector<std::string, 4> AsmNames(Registers.size()); for (unsigned i = 0, e = Registers.size(); i != e; ++i) { const CodeGenRegister &Reg = *Registers[i]; + std::string &AsmName = AsmNames[i]; - std::string AsmName; // "NoRegAltName" is special. We don't need to do a lookup for that, // as it's just a reference to the default register name. if (AltName == "" || AltName == "NoRegAltName") { @@ -495,21 +511,22 @@ emitRegisterNameString(raw_ostream &O, StringRef AltName, AsmName = AltNames[Idx]; } } + StringTable.add(AsmName); + } - O << StringTable.GetOrAddStringOffset(AsmName); - if (((i + 1) % 14) == 0) - O << ",\n "; - else - O << ", "; + StringTable.layout(); + O << " static const char AsmStrs" << AltName << "[] = {\n"; + StringTable.emit(O, printChar); + O << " };\n\n"; + O << " static const unsigned RegAsmOffset" << AltName << "[] = {"; + for (unsigned i = 0, e = Registers.size(); i != e; ++i) { + if ((i % 14) == 0) + O << "\n "; + O << StringTable.get(AsmNames[i]) << ", "; } - O << "0\n" - << " };\n" + O << "\n };\n" << "\n"; - - O << " const char *AsmStrs" << AltName << " =\n"; - StringTable.EmitString(O); - O << ";\n"; } void AsmWriterEmitter::EmitGetRegisterName(raw_ostream &O) { @@ -544,7 +561,7 @@ void AsmWriterEmitter::EmitGetRegisterName(raw_ostream &O) { O << " const unsigned *RegAsmOffset;\n" << " const char *AsmStrs;\n" << " switch(AltIdx) {\n" - << " default: assert(0 && \"Invalid register alt name index!\");\n"; + << " default: llvm_unreachable(\"Invalid register alt name index!\");\n"; for (unsigned i = 0, e = AltNameIndices.size(); i < e; ++i) { StringRef Namespace = AltNameIndices[1]->getValueAsString("Namespace"); StringRef AltName(AltNameIndices[i]->getName()); @@ -563,48 +580,6 @@ void AsmWriterEmitter::EmitGetRegisterName(raw_ostream &O) { << "}\n"; } -void AsmWriterEmitter::EmitGetInstructionName(raw_ostream &O) { - CodeGenTarget Target(Records); - Record *AsmWriter = Target.getAsmWriter(); - std::string ClassName = AsmWriter->getValueAsString("AsmWriterClassName"); - - const std::vector<const CodeGenInstruction*> &NumberedInstructions = - Target.getInstructionsByEnumValue(); - - StringToOffsetTable StringTable; - O << -"\n\n#ifdef GET_INSTRUCTION_NAME\n" -"#undef GET_INSTRUCTION_NAME\n\n" -"/// getInstructionName: This method is automatically generated by tblgen\n" -"/// from the instruction set description. This returns the enum name of the\n" -"/// specified instruction.\n" - "const char *" << Target.getName() << ClassName - << "::getInstructionName(unsigned Opcode) {\n" - << " assert(Opcode < " << NumberedInstructions.size() - << " && \"Invalid instruction number!\");\n" - << "\n" - << " static const unsigned InstAsmOffset[] = {"; - for (unsigned i = 0, e = NumberedInstructions.size(); i != e; ++i) { - const CodeGenInstruction &Inst = *NumberedInstructions[i]; - - std::string AsmName = Inst.TheDef->getName(); - if ((i % 14) == 0) - O << "\n "; - - O << StringTable.GetOrAddStringOffset(AsmName) << ", "; - } - O << "0\n" - << " };\n" - << "\n"; - - O << " const char *Strs =\n"; - StringTable.EmitString(O); - O << ";\n"; - - O << " return Strs+InstAsmOffset[Opcode];\n" - << "}\n\n#endif\n"; -} - namespace { // IAPrinter - Holds information about an InstAlias. Two InstAliases match if // they both have the same conditionals. In which case, we cannot print out the @@ -694,70 +669,7 @@ static void EmitGetMapOperandNumber(raw_ostream &O) { O << " I = OpMap.begin(), E = OpMap.end(); I != E; ++I)\n"; O << " if (I->first == Name)\n"; O << " return I->second;\n"; - O << " assert(false && \"Operand not in map!\");\n"; - O << " return 0;\n"; - O << "}\n\n"; -} - -void AsmWriterEmitter::EmitRegIsInRegClass(raw_ostream &O) { - CodeGenTarget Target(Records); - - // Enumerate the register classes. - ArrayRef<CodeGenRegisterClass*> RegisterClasses = - Target.getRegBank().getRegClasses(); - - O << "namespace { // Register classes\n"; - O << " enum RegClass {\n"; - - // Emit the register enum value for each RegisterClass. - for (unsigned I = 0, E = RegisterClasses.size(); I != E; ++I) { - if (I != 0) O << ",\n"; - O << " RC_" << RegisterClasses[I]->getName(); - } - - O << "\n };\n"; - O << "} // end anonymous namespace\n\n"; - - // Emit a function that returns 'true' if a regsiter is part of a particular - // register class. I.e., RAX is part of GR64 on X86. - O << "static bool regIsInRegisterClass" - << "(unsigned RegClass, unsigned Reg) {\n"; - - // Emit the switch that checks if a register belongs to a particular register - // class. - O << " switch (RegClass) {\n"; - O << " default: break;\n"; - - for (unsigned I = 0, E = RegisterClasses.size(); I != E; ++I) { - const CodeGenRegisterClass &RC = *RegisterClasses[I]; - - // Give the register class a legal C name if it's anonymous. - std::string Name = RC.getName(); - O << " case RC_" << Name << ":\n"; - - // Emit the register list now. - unsigned IE = RC.getOrder().size(); - if (IE == 1) { - O << " if (Reg == " << getQualifiedName(RC.getOrder()[0]) << ")\n"; - O << " return true;\n"; - } else { - O << " switch (Reg) {\n"; - O << " default: break;\n"; - - for (unsigned II = 0; II != IE; ++II) { - Record *Reg = RC.getOrder()[II]; - O << " case " << getQualifiedName(Reg) << ":\n"; - } - - O << " return true;\n"; - O << " }\n"; - } - - O << " break;\n"; - } - - O << " }\n\n"; - O << " return false;\n"; + O << " llvm_unreachable(\"Operand not in map!\");\n"; O << "}\n\n"; } @@ -804,8 +716,6 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) { O << "\n#ifdef PRINT_ALIAS_INSTR\n"; O << "#undef PRINT_ALIAS_INSTR\n\n"; - EmitRegIsInRegClass(O); - // Emit the method that prints the alias instruction. std::string ClassName = AsmWriter->getValueAsString("AsmWriterClassName"); @@ -858,7 +768,6 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) { const CodeGenInstAlias::ResultOperand &RO = CGA->ResultOperands[i]; switch (RO.Kind) { - default: assert(0 && "unexpected InstAlias operand kind"); case CodeGenInstAlias::ResultOperand::K_Record: { const Record *Rec = RO.getRecord(); StringRef ROName = RO.getName(); @@ -872,9 +781,9 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) { if (!IAP->isOpMapped(ROName)) { IAP->addOperand(ROName, i); - Cond = std::string("regIsInRegisterClass(RC_") + - CGA->ResultOperands[i].getRecord()->getName() + - ", MI->getOperand(" + llvm::utostr(i) + ").getReg())"; + Cond = std::string("MRI.getRegClass(") + Target.getName() + "::" + + CGA->ResultOperands[i].getRecord()->getName() + "RegClassID)" + ".contains(MI->getOperand(" + llvm::utostr(i) + ").getReg())"; IAP->addCond(Cond); } else { Cond = std::string("MI->getOperand(") + @@ -900,6 +809,13 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) { IAP->addCond(Cond); break; case CodeGenInstAlias::ResultOperand::K_Reg: + // If this is zero_reg, something's playing tricks we're not + // equipped to handle. + if (!CGA->ResultOperands[i].getRegister()) { + CantHandle = true; + break; + } + Cond = std::string("MI->getOperand(") + llvm::utostr(i) + ").getReg() == " + Target.getName() + "::" + CGA->ResultOperands[i].getRegister()->getName(); @@ -1015,7 +931,6 @@ void AsmWriterEmitter::run(raw_ostream &O) { EmitPrintInstruction(O); EmitGetRegisterName(O); - EmitGetInstructionName(O); EmitPrintAliasInstruction(O); } diff --git a/utils/TableGen/AsmWriterEmitter.h b/utils/TableGen/AsmWriterEmitter.h index 731e31c..9719b20 100644 --- a/utils/TableGen/AsmWriterEmitter.h +++ b/utils/TableGen/AsmWriterEmitter.h @@ -37,8 +37,6 @@ namespace llvm { private: void EmitPrintInstruction(raw_ostream &o); void EmitGetRegisterName(raw_ostream &o); - void EmitGetInstructionName(raw_ostream &o); - void EmitRegIsInRegClass(raw_ostream &O); void EmitPrintAliasInstruction(raw_ostream &O); AsmWriterInst *getAsmWriterInstByID(unsigned ID) const { diff --git a/utils/TableGen/CMakeLists.txt b/utils/TableGen/CMakeLists.txt index 02ebd67..2b70f1c 100644 --- a/utils/TableGen/CMakeLists.txt +++ b/utils/TableGen/CMakeLists.txt @@ -1,8 +1,8 @@ set(LLVM_REQUIRES_EH 1) set(LLVM_REQUIRES_RTTI 1) +set(LLVM_LINK_COMPONENTS Support) add_tablegen(llvm-tblgen LLVM - ARMDecoderEmitter.cpp AsmMatcherEmitter.cpp AsmWriterEmitter.cpp AsmWriterInst.cpp @@ -17,11 +17,11 @@ add_tablegen(llvm-tblgen LLVM DAGISelMatcherGen.cpp DAGISelMatcherOpt.cpp DAGISelMatcher.cpp + DFAPacketizerEmitter.cpp DisassemblerEmitter.cpp EDEmitter.cpp FastISelEmitter.cpp FixedLenDecoderEmitter.cpp - InstrEnumEmitter.cpp InstrInfoEmitter.cpp IntrinsicEmitter.cpp PseudoLoweringEmitter.cpp @@ -32,5 +32,6 @@ add_tablegen(llvm-tblgen LLVM TGValueTypes.cpp TableGen.cpp X86DisassemblerTables.cpp + X86ModRMFilters.cpp X86RecognizableInstr.cpp ) diff --git a/utils/TableGen/CallingConvEmitter.cpp b/utils/TableGen/CallingConvEmitter.cpp index fcdaa08..afbb3a8 100644 --- a/utils/TableGen/CallingConvEmitter.cpp +++ b/utils/TableGen/CallingConvEmitter.cpp @@ -96,7 +96,7 @@ void CallingConvEmitter::EmitAction(Record *Action, O << IndentStr << "if (unsigned Reg = State.AllocateReg("; O << getQualifiedName(RegList->getElementAsRecord(0)) << ")) {\n"; } else { - O << IndentStr << "static const unsigned RegList" << ++Counter + O << IndentStr << "static const uint16_t RegList" << ++Counter << "[] = {\n"; O << IndentStr << " "; for (unsigned i = 0, e = RegList->getSize(); i != e; ++i) { @@ -127,7 +127,7 @@ void CallingConvEmitter::EmitAction(Record *Action, unsigned RegListNumber = ++Counter; unsigned ShadowRegListNumber = ++Counter; - O << IndentStr << "static const unsigned RegList" << RegListNumber + O << IndentStr << "static const uint16_t RegList" << RegListNumber << "[] = {\n"; O << IndentStr << " "; for (unsigned i = 0, e = RegList->getSize(); i != e; ++i) { @@ -136,7 +136,7 @@ void CallingConvEmitter::EmitAction(Record *Action, } O << "\n" << IndentStr << "};\n"; - O << IndentStr << "static const unsigned RegList" + O << IndentStr << "static const uint16_t RegList" << ShadowRegListNumber << "[] = {\n"; O << IndentStr << " "; for (unsigned i = 0, e = ShadowRegList->getSize(); i != e; ++i) { diff --git a/utils/TableGen/CodeEmitterGen.cpp b/utils/TableGen/CodeEmitterGen.cpp index c5a1526..3943e8a 100644 --- a/utils/TableGen/CodeEmitterGen.cpp +++ b/utils/TableGen/CodeEmitterGen.cpp @@ -163,19 +163,19 @@ AddCodeToMergeInOperand(Record *R, BitsInit *BI, const std::string &VarName, --bit; } - unsigned opMask = ~0U >> (32-N); + uint64_t opMask = ~(uint64_t)0 >> (64-N); int opShift = beginVarBit - N + 1; opMask <<= opShift; opShift = beginInstBit - beginVarBit; if (opShift > 0) { - Case += " Value |= (op & " + utostr(opMask) + "U) << " + + Case += " Value |= (op & UINT64_C(" + utostr(opMask) + ")) << " + itostr(opShift) + ";\n"; } else if (opShift < 0) { - Case += " Value |= (op & " + utostr(opMask) + "U) >> " + + Case += " Value |= (op & UINT64_C(" + utostr(opMask) + ")) >> " + itostr(-opShift) + ";\n"; } else { - Case += " Value |= op & " + utostr(opMask) + "U;\n"; + Case += " Value |= op & UINT64_C(" + utostr(opMask) + ");\n"; } } } @@ -220,7 +220,7 @@ void CodeEmitterGen::run(raw_ostream &o) { Target.getInstructionsByEnumValue(); // Emit function declaration - o << "unsigned " << Target.getName(); + o << "uint64_t " << Target.getName(); if (MCEmitter) o << "MCCodeEmitter::getBinaryCodeForInstr(const MCInst &MI,\n" << " SmallVectorImpl<MCFixup> &Fixups) const {\n"; @@ -228,7 +228,7 @@ void CodeEmitterGen::run(raw_ostream &o) { o << "CodeEmitter::getBinaryCodeForInstr(const MachineInstr &MI) const {\n"; // Emit instruction base values - o << " static const unsigned InstBits[] = {\n"; + o << " static const uint64_t InstBits[] = {\n"; for (std::vector<const CodeGenInstruction*>::const_iterator IN = NumberedInstructions.begin(), EN = NumberedInstructions.end(); @@ -238,21 +238,21 @@ void CodeEmitterGen::run(raw_ostream &o) { if (R->getValueAsString("Namespace") == "TargetOpcode" || R->getValueAsBit("isPseudo")) { - o << " 0U,\n"; + o << " UINT64_C(0),\n"; continue; } BitsInit *BI = R->getValueAsBitsInit("Inst"); // Start by filling in fixed values. - unsigned Value = 0; + uint64_t Value = 0; for (unsigned i = 0, e = BI->getNumBits(); i != e; ++i) { if (BitInit *B = dynamic_cast<BitInit*>(BI->getBit(e-i-1))) - Value |= B->getValue() << (e-i-1); + Value |= (uint64_t)B->getValue() << (e-i-1); } - o << " " << Value << "U," << '\t' << "// " << R->getName() << "\n"; + o << " UINT64_C(" << Value << ")," << '\t' << "// " << R->getName() << "\n"; } - o << " 0U\n };\n"; + o << " UINT64_C(0)\n };\n"; // Map to accumulate all the cases. std::map<std::string, std::vector<std::string> > CaseMap; @@ -273,8 +273,8 @@ void CodeEmitterGen::run(raw_ostream &o) { // Emit initial function code o << " const unsigned opcode = MI.getOpcode();\n" - << " unsigned Value = InstBits[opcode];\n" - << " unsigned op = 0;\n" + << " uint64_t Value = InstBits[opcode];\n" + << " uint64_t op = 0;\n" << " (void)op; // suppress warning\n" << " switch (opcode) {\n"; diff --git a/utils/TableGen/CodeGenDAGPatterns.cpp b/utils/TableGen/CodeGenDAGPatterns.cpp index dbf1662..d2ddf23 100644 --- a/utils/TableGen/CodeGenDAGPatterns.cpp +++ b/utils/TableGen/CodeGenDAGPatterns.cpp @@ -18,8 +18,10 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Debug.h" -#include <set> +#include "llvm/Support/ErrorHandling.h" #include <algorithm> +#include <cstdio> +#include <set> using namespace llvm; //===----------------------------------------------------------------------===// @@ -629,11 +631,11 @@ TreePredicateFn::TreePredicateFn(TreePattern *N) : PatFragRec(N) { } std::string TreePredicateFn::getPredCode() const { - return PatFragRec->getRecord()->getValueAsCode("PredicateCode"); + return PatFragRec->getRecord()->getValueAsString("PredicateCode"); } std::string TreePredicateFn::getImmCode() const { - return PatFragRec->getRecord()->getValueAsCode("ImmediateCode"); + return PatFragRec->getRecord()->getValueAsString("ImmediateCode"); } @@ -748,7 +750,7 @@ std::string PatternToMatch::getPredicateCheck() const { #ifndef NDEBUG Def->dump(); #endif - assert(0 && "Unknown predicate type!"); + llvm_unreachable("Unknown predicate type!"); } if (!PredicateCheck.empty()) PredicateCheck += " && "; @@ -839,7 +841,6 @@ bool SDTypeConstraint::ApplyTypeConstraint(TreePatternNode *N, TreePatternNode *NodeToApply = getOperandNum(OperandNo, N, NodeInfo, ResNo); switch (ConstraintType) { - default: assert(0 && "Unknown constraint type!"); case SDTCisVT: // Operand must be a particular type. return NodeToApply->UpdateNodeType(ResNo, x.SDTCisVT_Info.VT, TP); @@ -913,7 +914,7 @@ bool SDTypeConstraint::ApplyTypeConstraint(TreePatternNode *N, EnforceVectorSubVectorTypeIs(NodeToApply->getExtType(ResNo), TP); } } - return false; + llvm_unreachable("Invalid ConstraintType!"); } //===----------------------------------------------------------------------===// @@ -1609,10 +1610,9 @@ bool TreePatternNode::ApplyTypeConstraints(TreePattern &TP, bool NotRegisters) { MadeChange |= Child->UpdateNodeType(ChildResNo, MVT::iPTR, TP); } else if (OperandNode->getName() == "unknown") { // Nothing to do. - } else { - assert(0 && "Unknown operand type!"); - abort(); - } + } else + llvm_unreachable("Unknown operand type!"); + MadeChange |= Child->ApplyTypeConstraints(TP, NotRegisters); } @@ -2071,7 +2071,7 @@ void CodeGenDAGPatterns::ParseNodeTransforms() { while (!Xforms.empty()) { Record *XFormNode = Xforms.back(); Record *SDNode = XFormNode->getValueAsDef("Opcode"); - std::string Code = XFormNode->getValueAsCode("XFormFunction"); + std::string Code = XFormNode->getValueAsString("XFormFunction"); SDNodeXForms.insert(std::make_pair(XFormNode, NodeXForm(SDNode, Code))); Xforms.pop_back(); diff --git a/utils/TableGen/CodeGenDAGPatterns.h b/utils/TableGen/CodeGenDAGPatterns.h index 936fd01..5a2d40a 100644 --- a/utils/TableGen/CodeGenDAGPatterns.h +++ b/utils/TableGen/CodeGenDAGPatterns.h @@ -19,6 +19,7 @@ #include "CodeGenIntrinsics.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" +#include "llvm/Support/ErrorHandling.h" #include <set> #include <algorithm> #include <vector> @@ -723,8 +724,7 @@ public: if (Intrinsics[i].TheDef == R) return Intrinsics[i]; for (unsigned i = 0, e = TgtIntrinsics.size(); i != e; ++i) if (TgtIntrinsics[i].TheDef == R) return TgtIntrinsics[i]; - assert(0 && "Unknown intrinsic!"); - abort(); + llvm_unreachable("Unknown intrinsic!"); } const CodeGenIntrinsic &getIntrinsicInfo(unsigned IID) const { @@ -732,8 +732,7 @@ public: return Intrinsics[IID-1]; if (IID-Intrinsics.size()-1 < TgtIntrinsics.size()) return TgtIntrinsics[IID-Intrinsics.size()-1]; - assert(0 && "Bad intrinsic ID!"); - abort(); + llvm_unreachable("Bad intrinsic ID!"); } unsigned getIntrinsicID(Record *R) const { @@ -741,8 +740,7 @@ public: if (Intrinsics[i].TheDef == R) return i; for (unsigned i = 0, e = TgtIntrinsics.size(); i != e; ++i) if (TgtIntrinsics[i].TheDef == R) return i + Intrinsics.size(); - assert(0 && "Unknown intrinsic!"); - abort(); + llvm_unreachable("Unknown intrinsic!"); } const DAGDefaultOperand &getDefaultOperand(Record *R) const { diff --git a/utils/TableGen/CodeGenInstruction.cpp b/utils/TableGen/CodeGenInstruction.cpp index 53d499f..fb9ad93 100644 --- a/utils/TableGen/CodeGenInstruction.cpp +++ b/utils/TableGen/CodeGenInstruction.cpp @@ -245,7 +245,7 @@ static void ParseConstraint(const std::string &CStr, CGIOperandList &Ops) { if (!Ops[DestOp.first].Constraints[DestOp.second].isNone()) throw "Operand '" + DestOpName + "' cannot have multiple constraints!"; Ops[DestOp.first].Constraints[DestOp.second] = - CGIOperandList::ConstraintInfo::getTied(FlatOpNo); + CGIOperandList::ConstraintInfo::getTied(FlatOpNo); } static void ParseConstraints(const std::string &CStr, CGIOperandList &Ops) { @@ -423,6 +423,18 @@ bool CodeGenInstAlias::tryAliasOpMatch(DagInit *Result, unsigned AliasOpNo, return true; } + // For register operands, the source register class can be a subclass + // of the instruction register class, not just an exact match. + if (ADI && ADI->getDef()->isSubClassOf("RegisterClass")) { + if (!InstOpRec->isSubClassOf("RegisterClass")) + return false; + if (!T.getRegisterClass(InstOpRec) + .hasSubClass(&T.getRegisterClass(ADI->getDef()))) + return false; + ResOp = ResultOperand(Result->getArgName(AliasOpNo), ADI->getDef()); + return true; + } + // Handle explicit registers. if (ADI && ADI->getDef()->isSubClassOf("Register")) { if (InstOpRec->isSubClassOf("OptionalDefOperand")) { @@ -456,14 +468,19 @@ bool CodeGenInstAlias::tryAliasOpMatch(DagInit *Result, unsigned AliasOpNo, if (ADI && ADI->getDef()->getName() == "zero_reg") { // Check if this is an optional def. - if (!InstOpRec->isSubClassOf("OptionalDefOperand")) - throw TGError(Loc, "reg0 used for result that is not an " - "OptionalDefOperand!"); + // Tied operands where the source is a sub-operand of a complex operand + // need to represent both operands in the alias destination instruction. + // Allow zero_reg for the tied portion. This can and should go away once + // the MC representation of things doesn't use tied operands at all. + //if (!InstOpRec->isSubClassOf("OptionalDefOperand")) + // throw TGError(Loc, "reg0 used for result that is not an " + // "OptionalDefOperand!"); ResOp = ResultOperand(static_cast<Record*>(0)); return true; } + // Literal integers. if (IntInit *II = dynamic_cast<IntInit*>(Arg)) { if (hasSubOps || !InstOpRec->isSubClassOf("Operand")) return false; @@ -475,6 +492,19 @@ bool CodeGenInstAlias::tryAliasOpMatch(DagInit *Result, unsigned AliasOpNo, return true; } + // If both are Operands with the same MVT, allow the conversion. It's + // up to the user to make sure the values are appropriate, just like + // for isel Pat's. + if (InstOpRec->isSubClassOf("Operand") && + ADI->getDef()->isSubClassOf("Operand")) { + // FIXME: What other attributes should we check here? Identical + // MIOperandInfo perhaps? + if (InstOpRec->getValueInit("Type") != ADI->getDef()->getValueInit("Type")) + return false; + ResOp = ResultOperand(Result->getArgName(AliasOpNo), ADI->getDef()); + return true; + } + return false; } @@ -511,8 +541,11 @@ CodeGenInstAlias::CodeGenInstAlias(Record *R, CodeGenTarget &T) : TheDef(R) { unsigned AliasOpNo = 0; for (unsigned i = 0, e = ResultInst->Operands.size(); i != e; ++i) { - // Tied registers don't have an entry in the result dag. - if (ResultInst->Operands[i].getTiedRegister() != -1) + // Tied registers don't have an entry in the result dag unless they're part + // of a complex operand, in which case we include them anyways, as we + // don't have any other way to specify the whole operand. + if (ResultInst->Operands[i].MINumOperands == 1 && + ResultInst->Operands[i].getTiedRegister() != -1) continue; if (AliasOpNo >= Result->getNumArgs()) diff --git a/utils/TableGen/CodeGenRegisters.cpp b/utils/TableGen/CodeGenRegisters.cpp index 8de4615..7ce4f878 100644 --- a/utils/TableGen/CodeGenRegisters.cpp +++ b/utils/TableGen/CodeGenRegisters.cpp @@ -15,6 +15,7 @@ #include "CodeGenRegisters.h" #include "CodeGenTarget.h" #include "llvm/TableGen/Error.h" +#include "llvm/ADT/IntEqClasses.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" @@ -22,6 +23,57 @@ using namespace llvm; //===----------------------------------------------------------------------===// +// CodeGenSubRegIndex +//===----------------------------------------------------------------------===// + +CodeGenSubRegIndex::CodeGenSubRegIndex(Record *R, unsigned Enum) + : TheDef(R), + EnumValue(Enum) +{} + +std::string CodeGenSubRegIndex::getNamespace() const { + if (TheDef->getValue("Namespace")) + return TheDef->getValueAsString("Namespace"); + else + return ""; +} + +const std::string &CodeGenSubRegIndex::getName() const { + return TheDef->getName(); +} + +std::string CodeGenSubRegIndex::getQualifiedName() const { + std::string N = getNamespace(); + if (!N.empty()) + N += "::"; + N += getName(); + return N; +} + +void CodeGenSubRegIndex::updateComponents(CodeGenRegBank &RegBank) { + std::vector<Record*> Comps = TheDef->getValueAsListOfDefs("ComposedOf"); + if (Comps.empty()) + return; + if (Comps.size() != 2) + throw TGError(TheDef->getLoc(), "ComposedOf must have exactly two entries"); + CodeGenSubRegIndex *A = RegBank.getSubRegIdx(Comps[0]); + CodeGenSubRegIndex *B = RegBank.getSubRegIdx(Comps[1]); + CodeGenSubRegIndex *X = A->addComposite(B, this); + if (X) + throw TGError(TheDef->getLoc(), "Ambiguous ComposedOf entries"); +} + +void CodeGenSubRegIndex::cleanComposites() { + // Clean out redundant mappings of the form this+X -> X. + for (CompMap::iterator i = Composed.begin(), e = Composed.end(); i != e;) { + CompMap::iterator j = i; + ++i; + if (j->first == j->second) + Composed.erase(j); + } +} + +//===----------------------------------------------------------------------===// // CodeGenRegister //===----------------------------------------------------------------------===// @@ -29,6 +81,7 @@ CodeGenRegister::CodeGenRegister(Record *R, unsigned Enum) : TheDef(R), EnumValue(Enum), CostPerUse(R->getValueAsInt("CostPerUse")), + CoveredBySubRegs(R->getValueAsBit("CoveredBySubRegs")), SubRegsComplete(false) {} @@ -37,12 +90,81 @@ const std::string &CodeGenRegister::getName() const { } namespace { - struct Orphan { - CodeGenRegister *SubReg; - Record *First, *Second; - Orphan(CodeGenRegister *r, Record *a, Record *b) - : SubReg(r), First(a), Second(b) {} - }; +// Iterate over all register units in a set of registers. +class RegUnitIterator { + CodeGenRegister::Set::const_iterator RegI, RegE; + CodeGenRegister::RegUnitList::const_iterator UnitI, UnitE; + +public: + RegUnitIterator(const CodeGenRegister::Set &Regs): + RegI(Regs.begin()), RegE(Regs.end()), UnitI(), UnitE() { + + if (RegI != RegE) { + UnitI = (*RegI)->getRegUnits().begin(); + UnitE = (*RegI)->getRegUnits().end(); + advance(); + } + } + + bool isValid() const { return UnitI != UnitE; } + + unsigned operator* () const { assert(isValid()); return *UnitI; }; + + const CodeGenRegister *getReg() const { assert(isValid()); return *RegI; } + + /// Preincrement. Move to the next unit. + void operator++() { + assert(isValid() && "Cannot advance beyond the last operand"); + ++UnitI; + advance(); + } + +protected: + void advance() { + while (UnitI == UnitE) { + if (++RegI == RegE) + break; + UnitI = (*RegI)->getRegUnits().begin(); + UnitE = (*RegI)->getRegUnits().end(); + } + } +}; +} // namespace + +// Merge two RegUnitLists maintaining the order and removing duplicates. +// Overwrites MergedRU in the process. +static void mergeRegUnits(CodeGenRegister::RegUnitList &MergedRU, + const CodeGenRegister::RegUnitList &RRU) { + CodeGenRegister::RegUnitList LRU = MergedRU; + MergedRU.clear(); + std::set_union(LRU.begin(), LRU.end(), RRU.begin(), RRU.end(), + std::back_inserter(MergedRU)); +} + +// Return true of this unit appears in RegUnits. +static bool hasRegUnit(CodeGenRegister::RegUnitList &RegUnits, unsigned Unit) { + return std::count(RegUnits.begin(), RegUnits.end(), Unit); +} + +// Inherit register units from subregisters. +// Return true if the RegUnits changed. +bool CodeGenRegister::inheritRegUnits(CodeGenRegBank &RegBank) { + unsigned OldNumUnits = RegUnits.size(); + for (SubRegMap::const_iterator I = SubRegs.begin(), E = SubRegs.end(); + I != E; ++I) { + // Strangely a register may have itself as a subreg (self-cycle) e.g. XMM. + // Only create a unit if no other subregs have units. + CodeGenRegister *SR = I->second; + if (SR == this) { + // RegUnits are only empty during getSubRegs, prior to computing weight. + if (RegUnits.empty()) + RegUnits.push_back(RegBank.newRegUnit(0)); + continue; + } + // Merge the subregister's units into this register's RegUnits. + mergeRegUnits(RegUnits, SR->RegUnits); + } + return OldNumUnits != RegUnits.size(); } const CodeGenRegister::SubRegMap & @@ -53,23 +175,26 @@ CodeGenRegister::getSubRegs(CodeGenRegBank &RegBank) { SubRegsComplete = true; std::vector<Record*> SubList = TheDef->getValueAsListOfDefs("SubRegs"); - std::vector<Record*> Indices = TheDef->getValueAsListOfDefs("SubRegIndices"); - if (SubList.size() != Indices.size()) + std::vector<Record*> IdxList = TheDef->getValueAsListOfDefs("SubRegIndices"); + if (SubList.size() != IdxList.size()) throw TGError(TheDef->getLoc(), "Register " + getName() + " SubRegIndices doesn't match SubRegs"); // First insert the direct subregs and make sure they are fully indexed. + SmallVector<CodeGenSubRegIndex*, 8> Indices; for (unsigned i = 0, e = SubList.size(); i != e; ++i) { CodeGenRegister *SR = RegBank.getReg(SubList[i]); - if (!SubRegs.insert(std::make_pair(Indices[i], SR)).second) - throw TGError(TheDef->getLoc(), "SubRegIndex " + Indices[i]->getName() + + CodeGenSubRegIndex *Idx = RegBank.getSubRegIdx(IdxList[i]); + Indices.push_back(Idx); + if (!SubRegs.insert(std::make_pair(Idx, SR)).second) + throw TGError(TheDef->getLoc(), "SubRegIndex " + Idx->getName() + " appears twice in Register " + getName()); } // Keep track of inherited subregs and how they can be reached. - SmallVector<Orphan, 8> Orphans; + SmallPtrSet<CodeGenRegister*, 8> Orphans; - // Clone inherited subregs and place duplicate entries on Orphans. + // Clone inherited subregs and place duplicate entries in Orphans. // Here the order is important - earlier subregs take precedence. for (unsigned i = 0, e = SubList.size(); i != e; ++i) { CodeGenRegister *SR = RegBank.getReg(SubList[i]); @@ -83,7 +208,7 @@ CodeGenRegister::getSubRegs(CodeGenRegBank &RegBank) { for (SubRegMap::const_iterator SI = Map.begin(), SE = Map.end(); SI != SE; ++SI) { if (!SubRegs.insert(*SI).second) - Orphans.push_back(Orphan(SI->second, Indices[i], SI->first)); + Orphans.insert(SI->second); // Noop sub-register indexes are possible, so avoid duplicates. if (SI->second != SR) @@ -91,6 +216,33 @@ CodeGenRegister::getSubRegs(CodeGenRegBank &RegBank) { } } + // Expand any composed subreg indices. + // If dsub_2 has ComposedOf = [qsub_1, dsub_0], and this register has a + // qsub_1 subreg, add a dsub_2 subreg. Keep growing Indices and process + // expanded subreg indices recursively. + for (unsigned i = 0; i != Indices.size(); ++i) { + CodeGenSubRegIndex *Idx = Indices[i]; + const CodeGenSubRegIndex::CompMap &Comps = Idx->getComposites(); + CodeGenRegister *SR = SubRegs[Idx]; + const SubRegMap &Map = SR->getSubRegs(RegBank); + + // Look at the possible compositions of Idx. + // They may not all be supported by SR. + for (CodeGenSubRegIndex::CompMap::const_iterator I = Comps.begin(), + E = Comps.end(); I != E; ++I) { + SubRegMap::const_iterator SRI = Map.find(I->first); + if (SRI == Map.end()) + continue; // Idx + I->first doesn't exist in SR. + // Add I->second as a name for the subreg SRI->second, assuming it is + // orphaned, and the name isn't already used for something else. + if (SubRegs.count(I->second) || !Orphans.erase(SRI->second)) + continue; + // We found a new name for the orphaned sub-register. + SubRegs.insert(std::make_pair(I->second, SRI->second)); + Indices.push_back(I->second); + } + } + // Process the composites. ListInit *Comps = TheDef->getValueAsListInit("CompositeIndices"); for (unsigned i = 0, e = Comps->size(); i != e; ++i) { @@ -103,6 +255,7 @@ CodeGenRegister::getSubRegs(CodeGenRegBank &RegBank) { if (!BaseIdxInit || !BaseIdxInit->getDef()->isSubClassOf("SubRegIndex")) throw TGError(TheDef->getLoc(), "Invalid SubClassIndex in " + Pat->getAsString()); + CodeGenSubRegIndex *BaseIdx = RegBank.getSubRegIdx(BaseIdxInit->getDef()); // Resolve list of subreg indices into R2. CodeGenRegister *R2 = this; @@ -112,8 +265,9 @@ CodeGenRegister::getSubRegs(CodeGenRegBank &RegBank) { if (!IdxInit || !IdxInit->getDef()->isSubClassOf("SubRegIndex")) throw TGError(TheDef->getLoc(), "Invalid SubClassIndex in " + Pat->getAsString()); + CodeGenSubRegIndex *Idx = RegBank.getSubRegIdx(IdxInit->getDef()); const SubRegMap &R2Subs = R2->getSubRegs(RegBank); - SubRegMap::const_iterator ni = R2Subs.find(IdxInit->getDef()); + SubRegMap::const_iterator ni = R2Subs.find(Idx); if (ni == R2Subs.end()) throw TGError(TheDef->getLoc(), "Composite " + Pat->getAsString() + " refers to bad index in " + R2->getName()); @@ -121,35 +275,76 @@ CodeGenRegister::getSubRegs(CodeGenRegBank &RegBank) { } // Insert composite index. Allow overriding inherited indices etc. - SubRegs[BaseIdxInit->getDef()] = R2; + SubRegs[BaseIdx] = R2; // R2 is no longer an orphan. - for (unsigned j = 0, je = Orphans.size(); j != je; ++j) - if (Orphans[j].SubReg == R2) - Orphans[j].SubReg = 0; + Orphans.erase(R2); } // Now Orphans contains the inherited subregisters without a direct index. // Create inferred indexes for all missing entries. - for (unsigned i = 0, e = Orphans.size(); i != e; ++i) { - Orphan &O = Orphans[i]; - if (!O.SubReg) - continue; - SubRegs[RegBank.getCompositeSubRegIndex(O.First, O.Second, true)] = - O.SubReg; + // Work backwards in the Indices vector in order to compose subregs bottom-up. + // Consider this subreg sequence: + // + // qsub_1 -> dsub_0 -> ssub_0 + // + // The qsub_1 -> dsub_0 composition becomes dsub_2, so the ssub_0 register + // can be reached in two different ways: + // + // qsub_1 -> ssub_0 + // dsub_2 -> ssub_0 + // + // We pick the latter composition because another register may have [dsub_0, + // dsub_1, dsub_2] subregs without neccessarily having a qsub_1 subreg. The + // dsub_2 -> ssub_0 composition can be shared. + while (!Indices.empty() && !Orphans.empty()) { + CodeGenSubRegIndex *Idx = Indices.pop_back_val(); + CodeGenRegister *SR = SubRegs[Idx]; + const SubRegMap &Map = SR->getSubRegs(RegBank); + for (SubRegMap::const_iterator SI = Map.begin(), SE = Map.end(); SI != SE; + ++SI) + if (Orphans.erase(SI->second)) + SubRegs[RegBank.getCompositeSubRegIndex(Idx, SI->first)] = SI->second; } + + // Initialize RegUnitList. A register with no subregisters creates its own + // unit. Otherwise, it inherits all its subregister's units. Because + // getSubRegs is called recursively, this processes the register hierarchy in + // postorder. + // + // TODO: We currently assume all register units correspond to a named "leaf" + // register. We should also unify register units for ad-hoc register + // aliases. This can be done by iteratively merging units for aliasing + // registers using a worklist. + assert(RegUnits.empty() && "Should only initialize RegUnits once"); + if (SubRegs.empty()) + RegUnits.push_back(RegBank.newRegUnit(0)); + else + inheritRegUnits(RegBank); return SubRegs; } void -CodeGenRegister::addSubRegsPreOrder(SetVector<CodeGenRegister*> &OSet) const { +CodeGenRegister::addSubRegsPreOrder(SetVector<const CodeGenRegister*> &OSet, + CodeGenRegBank &RegBank) const { assert(SubRegsComplete && "Must precompute sub-registers"); std::vector<Record*> Indices = TheDef->getValueAsListOfDefs("SubRegIndices"); for (unsigned i = 0, e = Indices.size(); i != e; ++i) { - CodeGenRegister *SR = SubRegs.find(Indices[i])->second; + CodeGenSubRegIndex *Idx = RegBank.getSubRegIdx(Indices[i]); + CodeGenRegister *SR = SubRegs.find(Idx)->second; if (OSet.insert(SR)) - SR->addSubRegsPreOrder(OSet); + SR->addSubRegsPreOrder(OSet, RegBank); + } +} + +// Get the sum of this register's unit weights. +unsigned CodeGenRegister::getWeight(const CodeGenRegBank &RegBank) const { + unsigned Weight = 0; + for (RegUnitList::const_iterator I = RegUnits.begin(), E = RegUnits.end(); + I != E; ++I) { + Weight += RegBank.getRegUnitWeight(*I); } + return Weight; } //===----------------------------------------------------------------------===// @@ -215,30 +410,40 @@ struct TupleExpander : SetTheory::Expander { for (unsigned i = 0, e = Proto->getValues().size(); i != e; ++i) { RecordVal RV = Proto->getValues()[i]; + // Skip existing fields, like NAME. + if (NewReg->getValue(RV.getNameInit())) + continue; + + StringRef Field = RV.getName(); + // Replace the sub-register list with Tuple. - if (RV.getName() == "SubRegs") + if (Field == "SubRegs") RV.setValue(ListInit::get(Tuple, RegisterRecTy)); // Provide a blank AsmName. MC hacks are required anyway. - if (RV.getName() == "AsmName") + if (Field == "AsmName") RV.setValue(BlankName); // CostPerUse is aggregated from all Tuple members. - if (RV.getName() == "CostPerUse") + if (Field == "CostPerUse") RV.setValue(IntInit::get(CostPerUse)); + // Composite registers are always covered by sub-registers. + if (Field == "CoveredBySubRegs") + RV.setValue(BitInit::get(true)); + // Copy fields from the RegisterTuples def. - if (RV.getName() == "SubRegIndices" || - RV.getName() == "CompositeIndices") { - NewReg->addValue(*Def->getValue(RV.getName())); + if (Field == "SubRegIndices" || + Field == "CompositeIndices") { + NewReg->addValue(*Def->getValue(Field)); continue; } // Some fields get their default uninitialized value. - if (RV.getName() == "DwarfNumbers" || - RV.getName() == "DwarfAlias" || - RV.getName() == "Aliases") { - if (const RecordVal *DefRV = RegisterCl->getValue(RV.getName())) + if (Field == "DwarfNumbers" || + Field == "DwarfAlias" || + Field == "Aliases") { + if (const RecordVal *DefRV = RegisterCl->getValue(Field)) NewReg->addValue(*DefRV); continue; } @@ -330,7 +535,7 @@ CodeGenRegisterClass::CodeGenRegisterClass(CodeGenRegBank &RegBank, Record *R) SpillAlignment = R->getValueAsInt("Alignment"); CopyCost = R->getValueAsInt("CopyCost"); Allocatable = R->getValueAsBit("isAllocatable"); - AltOrderSelect = R->getValueAsCode("AltOrderSelect"); + AltOrderSelect = R->getValueAsString("AltOrderSelect"); } // Create an inferred register class that was missing from the .td files. @@ -448,7 +653,7 @@ static int TopoOrderRC(const void *PA, const void *PB) { return 1; // Finally order by name as a tie breaker. - return A->getName() < B->getName(); + return StringRef(A->getName()).compare(B->getName()); } std::string CodeGenRegisterClass::getQualifiedName() const { @@ -504,6 +709,30 @@ void CodeGenRegisterClass::computeSubClasses(CodeGenRegBank &RegBank) { RegClasses[rci]->inheritProperties(RegBank); } +void +CodeGenRegisterClass::getSuperRegClasses(CodeGenSubRegIndex *SubIdx, + BitVector &Out) const { + DenseMap<CodeGenSubRegIndex*, + SmallPtrSet<CodeGenRegisterClass*, 8> >::const_iterator + FindI = SuperRegClasses.find(SubIdx); + if (FindI == SuperRegClasses.end()) + return; + for (SmallPtrSet<CodeGenRegisterClass*, 8>::const_iterator I = + FindI->second.begin(), E = FindI->second.end(); I != E; ++I) + Out.set((*I)->EnumValue); +} + +// Populate a unique sorted list of units from a register set. +void CodeGenRegisterClass::buildRegUnitSet( + std::vector<unsigned> &RegUnits) const { + std::vector<unsigned> TmpUnits; + for (RegUnitIterator UnitI(Members); UnitI.isValid(); ++UnitI) + TmpUnits.push_back(*UnitI); + std::sort(TmpUnits.begin(), TmpUnits.end()); + std::unique_copy(TmpUnits.begin(), TmpUnits.end(), + std::back_inserter(RegUnits)); +} + //===----------------------------------------------------------------------===// // CodeGenRegBank //===----------------------------------------------------------------------===// @@ -511,13 +740,19 @@ void CodeGenRegisterClass::computeSubClasses(CodeGenRegBank &RegBank) { CodeGenRegBank::CodeGenRegBank(RecordKeeper &Records) : Records(Records) { // Configure register Sets to understand register classes and tuples. Sets.addFieldExpander("RegisterClass", "MemberList"); + Sets.addFieldExpander("CalleeSavedRegs", "SaveList"); Sets.addExpander("RegisterTuples", new TupleExpander()); // Read in the user-defined (named) sub-register indices. // More indices will be synthesized later. - SubRegIndices = Records.getAllDerivedDefinitions("SubRegIndex"); - std::sort(SubRegIndices.begin(), SubRegIndices.end(), LessRecord()); - NumNamedIndices = SubRegIndices.size(); + std::vector<Record*> SRIs = Records.getAllDerivedDefinitions("SubRegIndex"); + std::sort(SRIs.begin(), SRIs.end(), LessRecord()); + NumNamedIndices = SRIs.size(); + for (unsigned i = 0, e = SRIs.size(); i != e; ++i) + getSubRegIdx(SRIs[i]); + // Build composite maps from ComposedOf fields. + for (unsigned i = 0, e = SubRegIndices.size(); i != e; ++i) + SubRegIndices[i]->updateComponents(*this); // Read in the register definitions. std::vector<Record*> Regs = Records.getAllDerivedDefinitions("Register"); @@ -538,9 +773,14 @@ CodeGenRegBank::CodeGenRegBank(RecordKeeper &Records) : Records(Records) { // Precompute all sub-register maps now all the registers are known. // This will create Composite entries for all inferred sub-register indices. + NumRegUnits = 0; for (unsigned i = 0, e = Registers.size(); i != e; ++i) Registers[i]->getSubRegs(*this); + // Native register units are associated with a leaf register. They've all been + // discovered now. + NumNativeRegUnits = NumRegUnits; + // Read in register class definitions. std::vector<Record*> RCs = Records.getAllDerivedDefinitions("RegisterClass"); if (RCs.empty()) @@ -561,6 +801,15 @@ CodeGenRegBank::CodeGenRegBank(RecordKeeper &Records) : Records(Records) { CodeGenRegisterClass::computeSubClasses(*this); } +CodeGenSubRegIndex *CodeGenRegBank::getSubRegIdx(Record *Def) { + CodeGenSubRegIndex *&Idx = Def2SubRegIdx[Def]; + if (Idx) + return Idx; + Idx = new CodeGenSubRegIndex(Def, SubRegIndices.size() + 1); + SubRegIndices.push_back(Idx); + return Idx; +} + CodeGenRegister *CodeGenRegBank::getReg(Record *Def) { CodeGenRegister *&Reg = Def2Reg[Def]; if (Reg) @@ -582,6 +831,23 @@ void CodeGenRegBank::addToMaps(CodeGenRegisterClass *RC) { Key2RC.insert(std::make_pair(K, RC)); } +// Create a synthetic sub-class if it is missing. +CodeGenRegisterClass* +CodeGenRegBank::getOrCreateSubClass(const CodeGenRegisterClass *RC, + const CodeGenRegister::Set *Members, + StringRef Name) { + // Synthetic sub-class has the same size and alignment as RC. + CodeGenRegisterClass::Key K(Members, RC->SpillSize, RC->SpillAlignment); + RCKeyMap::const_iterator FoundI = Key2RC.find(K); + if (FoundI != Key2RC.end()) + return FoundI->second; + + // Sub-class doesn't exist, create a new one. + CodeGenRegisterClass *NewRC = new CodeGenRegisterClass(Name, K); + addToMaps(NewRC); + return NewRC; +} + CodeGenRegisterClass *CodeGenRegBank::getRegClass(Record *Def) { if (CodeGenRegisterClass *RC = Def2RC[Def]) return RC; @@ -589,34 +855,28 @@ CodeGenRegisterClass *CodeGenRegBank::getRegClass(Record *Def) { throw TGError(Def->getLoc(), "Not a known RegisterClass!"); } -Record *CodeGenRegBank::getCompositeSubRegIndex(Record *A, Record *B, - bool create) { +CodeGenSubRegIndex* +CodeGenRegBank::getCompositeSubRegIndex(CodeGenSubRegIndex *A, + CodeGenSubRegIndex *B) { // Look for an existing entry. - Record *&Comp = Composite[std::make_pair(A, B)]; - if (Comp || !create) + CodeGenSubRegIndex *Comp = A->compose(B); + if (Comp) return Comp; // None exists, synthesize one. std::string Name = A->getName() + "_then_" + B->getName(); - Comp = new Record(Name, SMLoc(), Records); - SubRegIndices.push_back(Comp); + Comp = getSubRegIdx(new Record(Name, SMLoc(), Records)); + A->addComposite(B, Comp); return Comp; } -unsigned CodeGenRegBank::getSubRegIndexNo(Record *idx) { - std::vector<Record*>::const_iterator i = - std::find(SubRegIndices.begin(), SubRegIndices.end(), idx); - assert(i != SubRegIndices.end() && "Not a SubRegIndex"); - return (i - SubRegIndices.begin()) + 1; -} - void CodeGenRegBank::computeComposites() { for (unsigned i = 0, e = Registers.size(); i != e; ++i) { CodeGenRegister *Reg1 = Registers[i]; const CodeGenRegister::SubRegMap &SRM1 = Reg1->getSubRegs(); for (CodeGenRegister::SubRegMap::const_iterator i1 = SRM1.begin(), e1 = SRM1.end(); i1 != e1; ++i1) { - Record *Idx1 = i1->first; + CodeGenSubRegIndex *Idx1 = i1->first; CodeGenRegister *Reg2 = i1->second; // Ignore identity compositions. if (Reg1 == Reg2) @@ -625,7 +885,7 @@ void CodeGenRegBank::computeComposites() { // Try composing Idx1 with another SubRegIndex. for (CodeGenRegister::SubRegMap::const_iterator i2 = SRM2.begin(), e2 = SRM2.end(); i2 != e2; ++i2) { - std::pair<Record*, Record*> IdxPair(Idx1, i2->first); + CodeGenSubRegIndex *Idx2 = i2->first; CodeGenRegister *Reg3 = i2->second; // Ignore identity compositions. if (Reg2 == Reg3) @@ -634,16 +894,13 @@ void CodeGenRegBank::computeComposites() { for (CodeGenRegister::SubRegMap::const_iterator i1d = SRM1.begin(), e1d = SRM1.end(); i1d != e1d; ++i1d) { if (i1d->second == Reg3) { - std::pair<CompositeMap::iterator, bool> Ins = - Composite.insert(std::make_pair(IdxPair, i1d->first)); // Conflicting composition? Emit a warning but allow it. - if (!Ins.second && Ins.first->second != i1d->first) { - errs() << "Warning: SubRegIndex " << getQualifiedName(Idx1) - << " and " << getQualifiedName(IdxPair.second) + if (CodeGenSubRegIndex *Prev = Idx1->addComposite(Idx2, i1d->first)) + errs() << "Warning: SubRegIndex " << Idx1->getQualifiedName() + << " and " << Idx2->getQualifiedName() << " compose ambiguously as " - << getQualifiedName(Ins.first->second) << " or " - << getQualifiedName(i1d->first) << "\n"; - } + << Prev->getQualifiedName() << " or " + << i1d->first->getQualifiedName() << "\n"; } } } @@ -652,12 +909,388 @@ void CodeGenRegBank::computeComposites() { // We don't care about the difference between (Idx1, Idx2) -> Idx2 and invalid // compositions, so remove any mappings of that form. - for (CompositeMap::iterator i = Composite.begin(), e = Composite.end(); - i != e;) { - CompositeMap::iterator j = i; - ++i; - if (j->first.second == j->second) - Composite.erase(j); + for (unsigned i = 0, e = SubRegIndices.size(); i != e; ++i) + SubRegIndices[i]->cleanComposites(); +} + +namespace { +// UberRegSet is a helper class for computeRegUnitWeights. Each UberRegSet is +// the transitive closure of the union of overlapping register +// classes. Together, the UberRegSets form a partition of the registers. If we +// consider overlapping register classes to be connected, then each UberRegSet +// is a set of connected components. +// +// An UberRegSet will likely be a horizontal slice of register names of +// the same width. Nontrivial subregisters should then be in a separate +// UberRegSet. But this property isn't required for valid computation of +// register unit weights. +// +// A Weight field caches the max per-register unit weight in each UberRegSet. +// +// A set of SingularDeterminants flags single units of some register in this set +// for which the unit weight equals the set weight. These units should not have +// their weight increased. +struct UberRegSet { + CodeGenRegister::Set Regs; + unsigned Weight; + CodeGenRegister::RegUnitList SingularDeterminants; + + UberRegSet(): Weight(0) {} +}; +} // namespace + +// Partition registers into UberRegSets, where each set is the transitive +// closure of the union of overlapping register classes. +// +// UberRegSets[0] is a special non-allocatable set. +static void computeUberSets(std::vector<UberRegSet> &UberSets, + std::vector<UberRegSet*> &RegSets, + CodeGenRegBank &RegBank) { + + const std::vector<CodeGenRegister*> &Registers = RegBank.getRegisters(); + + // The Register EnumValue is one greater than its index into Registers. + assert(Registers.size() == Registers[Registers.size()-1]->EnumValue && + "register enum value mismatch"); + + // For simplicitly make the SetID the same as EnumValue. + IntEqClasses UberSetIDs(Registers.size()+1); + std::set<unsigned> AllocatableRegs; + for (unsigned i = 0, e = RegBank.getRegClasses().size(); i != e; ++i) { + + CodeGenRegisterClass *RegClass = RegBank.getRegClasses()[i]; + if (!RegClass->Allocatable) + continue; + + const CodeGenRegister::Set &Regs = RegClass->getMembers(); + if (Regs.empty()) + continue; + + unsigned USetID = UberSetIDs.findLeader((*Regs.begin())->EnumValue); + assert(USetID && "register number 0 is invalid"); + + AllocatableRegs.insert((*Regs.begin())->EnumValue); + for (CodeGenRegister::Set::const_iterator I = llvm::next(Regs.begin()), + E = Regs.end(); I != E; ++I) { + AllocatableRegs.insert((*I)->EnumValue); + UberSetIDs.join(USetID, (*I)->EnumValue); + } + } + // Combine non-allocatable regs. + for (unsigned i = 0, e = Registers.size(); i != e; ++i) { + unsigned RegNum = Registers[i]->EnumValue; + if (AllocatableRegs.count(RegNum)) + continue; + + UberSetIDs.join(0, RegNum); + } + UberSetIDs.compress(); + + // Make the first UberSet a special unallocatable set. + unsigned ZeroID = UberSetIDs[0]; + + // Insert Registers into the UberSets formed by union-find. + // Do not resize after this. + UberSets.resize(UberSetIDs.getNumClasses()); + for (unsigned i = 0, e = Registers.size(); i != e; ++i) { + const CodeGenRegister *Reg = Registers[i]; + unsigned USetID = UberSetIDs[Reg->EnumValue]; + if (!USetID) + USetID = ZeroID; + else if (USetID == ZeroID) + USetID = 0; + + UberRegSet *USet = &UberSets[USetID]; + USet->Regs.insert(Reg); + RegSets[i] = USet; + } +} + +// Recompute each UberSet weight after changing unit weights. +static void computeUberWeights(std::vector<UberRegSet> &UberSets, + CodeGenRegBank &RegBank) { + // Skip the first unallocatable set. + for (std::vector<UberRegSet>::iterator I = llvm::next(UberSets.begin()), + E = UberSets.end(); I != E; ++I) { + + // Initialize all unit weights in this set, and remember the max units/reg. + const CodeGenRegister *Reg = 0; + unsigned MaxWeight = 0, Weight = 0; + for (RegUnitIterator UnitI(I->Regs); UnitI.isValid(); ++UnitI) { + if (Reg != UnitI.getReg()) { + if (Weight > MaxWeight) + MaxWeight = Weight; + Reg = UnitI.getReg(); + Weight = 0; + } + unsigned UWeight = RegBank.getRegUnitWeight(*UnitI); + if (!UWeight) { + UWeight = 1; + RegBank.increaseRegUnitWeight(*UnitI, UWeight); + } + Weight += UWeight; + } + if (Weight > MaxWeight) + MaxWeight = Weight; + + // Update the set weight. + I->Weight = MaxWeight; + + // Find singular determinants. + for (CodeGenRegister::Set::iterator RegI = I->Regs.begin(), + RegE = I->Regs.end(); RegI != RegE; ++RegI) { + if ((*RegI)->getRegUnits().size() == 1 + && (*RegI)->getWeight(RegBank) == I->Weight) + mergeRegUnits(I->SingularDeterminants, (*RegI)->getRegUnits()); + } + } +} + +// normalizeWeight is a computeRegUnitWeights helper that adjusts the weight of +// a register and its subregisters so that they have the same weight as their +// UberSet. Self-recursion processes the subregister tree in postorder so +// subregisters are normalized first. +// +// Side effects: +// - creates new adopted register units +// - causes superregisters to inherit adopted units +// - increases the weight of "singular" units +// - induces recomputation of UberWeights. +static bool normalizeWeight(CodeGenRegister *Reg, + std::vector<UberRegSet> &UberSets, + std::vector<UberRegSet*> &RegSets, + CodeGenRegister::RegUnitList &NormalUnits, + CodeGenRegBank &RegBank) { + bool Changed = false; + const CodeGenRegister::SubRegMap &SRM = Reg->getSubRegs(); + for (CodeGenRegister::SubRegMap::const_iterator SRI = SRM.begin(), + SRE = SRM.end(); SRI != SRE; ++SRI) { + if (SRI->second == Reg) + continue; // self-cycles happen + + Changed |= + normalizeWeight(SRI->second, UberSets, RegSets, NormalUnits, RegBank); + } + // Postorder register normalization. + + // Inherit register units newly adopted by subregisters. + if (Reg->inheritRegUnits(RegBank)) + computeUberWeights(UberSets, RegBank); + + // Check if this register is too skinny for its UberRegSet. + UberRegSet *UberSet = RegSets[RegBank.getRegIndex(Reg)]; + + unsigned RegWeight = Reg->getWeight(RegBank); + if (UberSet->Weight > RegWeight) { + // A register unit's weight can be adjusted only if it is the singular unit + // for this register, has not been used to normalize a subregister's set, + // and has not already been used to singularly determine this UberRegSet. + unsigned AdjustUnit = Reg->getRegUnits().front(); + if (Reg->getRegUnits().size() != 1 + || hasRegUnit(NormalUnits, AdjustUnit) + || hasRegUnit(UberSet->SingularDeterminants, AdjustUnit)) { + // We don't have an adjustable unit, so adopt a new one. + AdjustUnit = RegBank.newRegUnit(UberSet->Weight - RegWeight); + Reg->adoptRegUnit(AdjustUnit); + // Adopting a unit does not immediately require recomputing set weights. + } + else { + // Adjust the existing single unit. + RegBank.increaseRegUnitWeight(AdjustUnit, UberSet->Weight - RegWeight); + // The unit may be shared among sets and registers within this set. + computeUberWeights(UberSets, RegBank); + } + Changed = true; + } + + // Mark these units normalized so superregisters can't change their weights. + mergeRegUnits(NormalUnits, Reg->getRegUnits()); + + return Changed; +} + +// Compute a weight for each register unit created during getSubRegs. +// +// The goal is that two registers in the same class will have the same weight, +// where each register's weight is defined as sum of its units' weights. +void CodeGenRegBank::computeRegUnitWeights() { + assert(RegUnitWeights.empty() && "Only initialize RegUnitWeights once"); + + // Only allocatable units will be initialized to nonzero weight. + RegUnitWeights.resize(NumRegUnits); + + std::vector<UberRegSet> UberSets; + std::vector<UberRegSet*> RegSets(Registers.size()); + computeUberSets(UberSets, RegSets, *this); + // UberSets and RegSets are now immutable. + + computeUberWeights(UberSets, *this); + + // Iterate over each Register, normalizing the unit weights until reaching + // a fix point. + unsigned NumIters = 0; + for (bool Changed = true; Changed; ++NumIters) { + assert(NumIters <= NumNativeRegUnits && "Runaway register unit weights"); + Changed = false; + for (unsigned i = 0, e = Registers.size(); i != e; ++i) { + CodeGenRegister::RegUnitList NormalUnits; + Changed |= + normalizeWeight(Registers[i], UberSets, RegSets, NormalUnits, *this); + } + } +} + +// Find a set in UniqueSets with the same elements as Set. +// Return an iterator into UniqueSets. +static std::vector<RegUnitSet>::const_iterator +findRegUnitSet(const std::vector<RegUnitSet> &UniqueSets, + const RegUnitSet &Set) { + std::vector<RegUnitSet>::const_iterator + I = UniqueSets.begin(), E = UniqueSets.end(); + for(;I != E; ++I) { + if (I->Units == Set.Units) + break; + } + return I; +} + +// Return true if the RUSubSet is a subset of RUSuperSet. +static bool isRegUnitSubSet(const std::vector<unsigned> &RUSubSet, + const std::vector<unsigned> &RUSuperSet) { + return std::includes(RUSuperSet.begin(), RUSuperSet.end(), + RUSubSet.begin(), RUSubSet.end()); +} + +// Iteratively prune unit sets. +void CodeGenRegBank::pruneUnitSets() { + assert(RegClassUnitSets.empty() && "this invalidates RegClassUnitSets"); + + // Form an equivalence class of UnitSets with no significant difference. + std::vector<unsigned> SuperSetIDs; + for (unsigned SubIdx = 0, EndIdx = RegUnitSets.size(); + SubIdx != EndIdx; ++SubIdx) { + const RegUnitSet &SubSet = RegUnitSets[SubIdx]; + unsigned SuperIdx = 0; + for (; SuperIdx != EndIdx; ++SuperIdx) { + if (SuperIdx == SubIdx) + continue; + + const RegUnitSet &SuperSet = RegUnitSets[SuperIdx]; + if (isRegUnitSubSet(SubSet.Units, SuperSet.Units) + && (SubSet.Units.size() + 3 > SuperSet.Units.size())) { + break; + } + } + if (SuperIdx == EndIdx) + SuperSetIDs.push_back(SubIdx); + } + // Populate PrunedUnitSets with each equivalence class's superset. + std::vector<RegUnitSet> PrunedUnitSets(SuperSetIDs.size()); + for (unsigned i = 0, e = SuperSetIDs.size(); i != e; ++i) { + unsigned SuperIdx = SuperSetIDs[i]; + PrunedUnitSets[i].Name = RegUnitSets[SuperIdx].Name; + PrunedUnitSets[i].Units.swap(RegUnitSets[SuperIdx].Units); + } + RegUnitSets.swap(PrunedUnitSets); +} + +// Create a RegUnitSet for each RegClass that contains all units in the class +// including adopted units that are necessary to model register pressure. Then +// iteratively compute RegUnitSets such that the union of any two overlapping +// RegUnitSets is repreresented. +// +// RegisterInfoEmitter will map each RegClass to its RegUnitClass and any +// RegUnitSet that is a superset of that RegUnitClass. +void CodeGenRegBank::computeRegUnitSets() { + + // Compute a unique RegUnitSet for each RegClass. + const ArrayRef<CodeGenRegisterClass*> &RegClasses = getRegClasses(); + unsigned NumRegClasses = RegClasses.size(); + for (unsigned RCIdx = 0, RCEnd = NumRegClasses; RCIdx != RCEnd; ++RCIdx) { + if (!RegClasses[RCIdx]->Allocatable) + continue; + + // Speculatively grow the RegUnitSets to hold the new set. + RegUnitSets.resize(RegUnitSets.size() + 1); + RegUnitSets.back().Name = RegClasses[RCIdx]->getName(); + + // Compute a sorted list of units in this class. + RegClasses[RCIdx]->buildRegUnitSet(RegUnitSets.back().Units); + + // Find an existing RegUnitSet. + std::vector<RegUnitSet>::const_iterator SetI = + findRegUnitSet(RegUnitSets, RegUnitSets.back()); + if (SetI != llvm::prior(RegUnitSets.end())) + RegUnitSets.pop_back(); + } + + // Iteratively prune unit sets. + pruneUnitSets(); + + // Iterate over all unit sets, including new ones added by this loop. + unsigned NumRegUnitSubSets = RegUnitSets.size(); + for (unsigned Idx = 0, EndIdx = RegUnitSets.size(); Idx != EndIdx; ++Idx) { + // In theory, this is combinatorial. In practice, it needs to be bounded + // by a small number of sets for regpressure to be efficient. + // If the assert is hit, we need to implement pruning. + assert(Idx < (2*NumRegUnitSubSets) && "runaway unit set inference"); + + // Compare new sets with all original classes. + for (unsigned SearchIdx = (Idx >= NumRegUnitSubSets) ? 0 : Idx+1; + SearchIdx != EndIdx; ++SearchIdx) { + std::set<unsigned> Intersection; + std::set_intersection(RegUnitSets[Idx].Units.begin(), + RegUnitSets[Idx].Units.end(), + RegUnitSets[SearchIdx].Units.begin(), + RegUnitSets[SearchIdx].Units.end(), + std::inserter(Intersection, Intersection.begin())); + if (Intersection.empty()) + continue; + + // Speculatively grow the RegUnitSets to hold the new set. + RegUnitSets.resize(RegUnitSets.size() + 1); + RegUnitSets.back().Name = + RegUnitSets[Idx].Name + "+" + RegUnitSets[SearchIdx].Name; + + std::set_union(RegUnitSets[Idx].Units.begin(), + RegUnitSets[Idx].Units.end(), + RegUnitSets[SearchIdx].Units.begin(), + RegUnitSets[SearchIdx].Units.end(), + std::inserter(RegUnitSets.back().Units, + RegUnitSets.back().Units.begin())); + + // Find an existing RegUnitSet, or add the union to the unique sets. + std::vector<RegUnitSet>::const_iterator SetI = + findRegUnitSet(RegUnitSets, RegUnitSets.back()); + if (SetI != llvm::prior(RegUnitSets.end())) + RegUnitSets.pop_back(); + } + } + + // Iteratively prune unit sets after inferring supersets. + pruneUnitSets(); + + // For each register class, list the UnitSets that are supersets. + RegClassUnitSets.resize(NumRegClasses); + for (unsigned RCIdx = 0, RCEnd = NumRegClasses; RCIdx != RCEnd; ++RCIdx) { + if (!RegClasses[RCIdx]->Allocatable) + continue; + + // Recompute the sorted list of units in this class. + std::vector<unsigned> RegUnits; + RegClasses[RCIdx]->buildRegUnitSet(RegUnits); + + // Don't increase pressure for unallocatable regclasses. + if (RegUnits.empty()) + continue; + + // Find all supersets. + for (unsigned USIdx = 0, USEnd = RegUnitSets.size(); + USIdx != USEnd; ++USIdx) { + if (isRegUnitSubSet(RegUnits, RegUnitSets[USIdx].Units)) + RegClassUnitSets[RCIdx].push_back(USIdx); + } + assert(!RegClassUnitSets[RCIdx].empty() && "missing unit set for regclass"); } } @@ -737,62 +1370,187 @@ computeOverlaps(std::map<const CodeGenRegister*, CodeGenRegister::Set> &Map) { void CodeGenRegBank::computeDerivedInfo() { computeComposites(); + + // Compute a weight for each register unit created during getSubRegs. + // This may create adopted register units (with unit # >= NumNativeRegUnits). + computeRegUnitWeights(); + + // Compute a unique set of RegUnitSets. One for each RegClass and inferred + // supersets for the union of overlapping sets. + computeRegUnitSets(); } -// Infer missing register classes. // -// For every register class RC, make sure that the set of registers in RC with -// a given SubIxx sub-register form a register class. -void CodeGenRegBank::computeInferredRegisterClasses() { - // When this function is called, the register classes have not been sorted - // and assigned EnumValues yet. That means getSubClasses(), - // getSuperClasses(), and hasSubClass() functions are defunct. +// Synthesize missing register class intersections. +// +// Make sure that sub-classes of RC exists such that getCommonSubClass(RC, X) +// returns a maximal register class for all X. +// +void CodeGenRegBank::inferCommonSubClass(CodeGenRegisterClass *RC) { + for (unsigned rci = 0, rce = RegClasses.size(); rci != rce; ++rci) { + CodeGenRegisterClass *RC1 = RC; + CodeGenRegisterClass *RC2 = RegClasses[rci]; + if (RC1 == RC2) + continue; - // Map SubRegIndex to register set. - typedef std::map<Record*, CodeGenRegister::Set, LessRecord> SubReg2SetMap; + // Compute the set intersection of RC1 and RC2. + const CodeGenRegister::Set &Memb1 = RC1->getMembers(); + const CodeGenRegister::Set &Memb2 = RC2->getMembers(); + CodeGenRegister::Set Intersection; + std::set_intersection(Memb1.begin(), Memb1.end(), + Memb2.begin(), Memb2.end(), + std::inserter(Intersection, Intersection.begin()), + CodeGenRegister::Less()); + + // Skip disjoint class pairs. + if (Intersection.empty()) + continue; - // Visit all register classes, including the ones being added by the loop. - for (unsigned rci = 0; rci != RegClasses.size(); ++rci) { - CodeGenRegisterClass &RC = *RegClasses[rci]; + // If RC1 and RC2 have different spill sizes or alignments, use the + // larger size for sub-classing. If they are equal, prefer RC1. + if (RC2->SpillSize > RC1->SpillSize || + (RC2->SpillSize == RC1->SpillSize && + RC2->SpillAlignment > RC1->SpillAlignment)) + std::swap(RC1, RC2); - // Compute the set of registers supporting each SubRegIndex. - SubReg2SetMap SRSets; - for (CodeGenRegister::Set::const_iterator RI = RC.getMembers().begin(), - RE = RC.getMembers().end(); RI != RE; ++RI) { - const CodeGenRegister::SubRegMap &SRM = (*RI)->getSubRegs(); - for (CodeGenRegister::SubRegMap::const_iterator I = SRM.begin(), - E = SRM.end(); I != E; ++I) - SRSets[I->first].insert(*RI); + getOrCreateSubClass(RC1, &Intersection, + RC1->getName() + "_and_" + RC2->getName()); + } +} + +// +// Synthesize missing sub-classes for getSubClassWithSubReg(). +// +// Make sure that the set of registers in RC with a given SubIdx sub-register +// form a register class. Update RC->SubClassWithSubReg. +// +void CodeGenRegBank::inferSubClassWithSubReg(CodeGenRegisterClass *RC) { + // Map SubRegIndex to set of registers in RC supporting that SubRegIndex. + typedef std::map<CodeGenSubRegIndex*, CodeGenRegister::Set, + CodeGenSubRegIndex::Less> SubReg2SetMap; + + // Compute the set of registers supporting each SubRegIndex. + SubReg2SetMap SRSets; + for (CodeGenRegister::Set::const_iterator RI = RC->getMembers().begin(), + RE = RC->getMembers().end(); RI != RE; ++RI) { + const CodeGenRegister::SubRegMap &SRM = (*RI)->getSubRegs(); + for (CodeGenRegister::SubRegMap::const_iterator I = SRM.begin(), + E = SRM.end(); I != E; ++I) + SRSets[I->first].insert(*RI); + } + + // Find matching classes for all SRSets entries. Iterate in SubRegIndex + // numerical order to visit synthetic indices last. + for (unsigned sri = 0, sre = SubRegIndices.size(); sri != sre; ++sri) { + CodeGenSubRegIndex *SubIdx = SubRegIndices[sri]; + SubReg2SetMap::const_iterator I = SRSets.find(SubIdx); + // Unsupported SubRegIndex. Skip it. + if (I == SRSets.end()) + continue; + // In most cases, all RC registers support the SubRegIndex. + if (I->second.size() == RC->getMembers().size()) { + RC->setSubClassWithSubReg(SubIdx, RC); + continue; + } + // This is a real subset. See if we have a matching class. + CodeGenRegisterClass *SubRC = + getOrCreateSubClass(RC, &I->second, + RC->getName() + "_with_" + I->first->getName()); + RC->setSubClassWithSubReg(SubIdx, SubRC); + } +} + +// +// Synthesize missing sub-classes of RC for getMatchingSuperRegClass(). +// +// Create sub-classes of RC such that getMatchingSuperRegClass(RC, SubIdx, X) +// has a maximal result for any SubIdx and any X >= FirstSubRegRC. +// + +void CodeGenRegBank::inferMatchingSuperRegClass(CodeGenRegisterClass *RC, + unsigned FirstSubRegRC) { + SmallVector<std::pair<const CodeGenRegister*, + const CodeGenRegister*>, 16> SSPairs; + + // Iterate in SubRegIndex numerical order to visit synthetic indices last. + for (unsigned sri = 0, sre = SubRegIndices.size(); sri != sre; ++sri) { + CodeGenSubRegIndex *SubIdx = SubRegIndices[sri]; + // Skip indexes that aren't fully supported by RC's registers. This was + // computed by inferSubClassWithSubReg() above which should have been + // called first. + if (RC->getSubClassWithSubReg(SubIdx) != RC) + continue; + + // Build list of (Super, Sub) pairs for this SubIdx. + SSPairs.clear(); + for (CodeGenRegister::Set::const_iterator RI = RC->getMembers().begin(), + RE = RC->getMembers().end(); RI != RE; ++RI) { + const CodeGenRegister *Super = *RI; + const CodeGenRegister *Sub = Super->getSubRegs().find(SubIdx)->second; + assert(Sub && "Missing sub-register"); + SSPairs.push_back(std::make_pair(Super, Sub)); } - // Find matching classes for all SRSets entries. Iterate in SubRegIndex - // numerical order to visit synthetic indices last. - for (unsigned sri = 0, sre = SubRegIndices.size(); sri != sre; ++sri) { - Record *SubIdx = SubRegIndices[sri]; - SubReg2SetMap::const_iterator I = SRSets.find(SubIdx); - // Unsupported SubRegIndex. Skip it. - if (I == SRSets.end()) + // Iterate over sub-register class candidates. Ignore classes created by + // this loop. They will never be useful. + for (unsigned rci = FirstSubRegRC, rce = RegClasses.size(); rci != rce; + ++rci) { + CodeGenRegisterClass *SubRC = RegClasses[rci]; + // Compute the subset of RC that maps into SubRC. + CodeGenRegister::Set SubSet; + for (unsigned i = 0, e = SSPairs.size(); i != e; ++i) + if (SubRC->contains(SSPairs[i].second)) + SubSet.insert(SSPairs[i].first); + if (SubSet.empty()) continue; - // In most cases, all RC registers support the SubRegIndex. - if (I->second.size() == RC.getMembers().size()) { - RC.setSubClassWithSubReg(SubIdx, &RC); + // RC injects completely into SubRC. + if (SubSet.size() == SSPairs.size()) { + SubRC->addSuperRegClass(SubIdx, RC); continue; } + // Only a subset of RC maps into SubRC. Make sure it is represented by a + // class. + getOrCreateSubClass(RC, &SubSet, RC->getName() + + "_with_" + SubIdx->getName() + + "_in_" + SubRC->getName()); + } + } +} - // This is a real subset. See if we have a matching class. - CodeGenRegisterClass::Key K(&I->second, RC.SpillSize, RC.SpillAlignment); - RCKeyMap::const_iterator FoundI = Key2RC.find(K); - if (FoundI != Key2RC.end()) { - RC.setSubClassWithSubReg(SubIdx, FoundI->second); - continue; - } - // Class doesn't exist. - CodeGenRegisterClass *NewRC = - new CodeGenRegisterClass(RC.getName() + "_with_" + - I->first->getName(), K); - addToMaps(NewRC); - RC.setSubClassWithSubReg(SubIdx, NewRC); +// +// Infer missing register classes. +// +void CodeGenRegBank::computeInferredRegisterClasses() { + // When this function is called, the register classes have not been sorted + // and assigned EnumValues yet. That means getSubClasses(), + // getSuperClasses(), and hasSubClass() functions are defunct. + unsigned FirstNewRC = RegClasses.size(); + + // Visit all register classes, including the ones being added by the loop. + for (unsigned rci = 0; rci != RegClasses.size(); ++rci) { + CodeGenRegisterClass *RC = RegClasses[rci]; + + // Synthesize answers for getSubClassWithSubReg(). + inferSubClassWithSubReg(RC); + + // Synthesize answers for getCommonSubClass(). + inferCommonSubClass(RC); + + // Synthesize answers for getMatchingSuperRegClass(). + inferMatchingSuperRegClass(RC); + + // New register classes are created while this loop is running, and we need + // to visit all of them. I particular, inferMatchingSuperRegClass needs + // to match old super-register classes with sub-register classes created + // after inferMatchingSuperRegClass was called. At this point, + // inferMatchingSuperRegClass has checked SuperRC = [0..rci] with SubRC = + // [0..FirstNewRC). We need to cover SubRC = [FirstNewRC..rci]. + if (rci + 1 == FirstNewRC) { + unsigned NextNewRC = RegClasses.size(); + for (unsigned rci2 = 0; rci2 != FirstNewRC; ++rci2) + inferMatchingSuperRegClass(RegClasses[rci2], FirstNewRC); + FirstNewRC = NextNewRC; } } } @@ -843,3 +1601,45 @@ CodeGenRegBank::getRegClassForRegister(Record *R) { } return FoundRC; } + +BitVector CodeGenRegBank::computeCoveredRegisters(ArrayRef<Record*> Regs) { + SetVector<const CodeGenRegister*> Set; + + // First add Regs with all sub-registers. + for (unsigned i = 0, e = Regs.size(); i != e; ++i) { + CodeGenRegister *Reg = getReg(Regs[i]); + if (Set.insert(Reg)) + // Reg is new, add all sub-registers. + // The pre-ordering is not important here. + Reg->addSubRegsPreOrder(Set, *this); + } + + // Second, find all super-registers that are completely covered by the set. + for (unsigned i = 0; i != Set.size(); ++i) { + const CodeGenRegister::SuperRegList &SR = Set[i]->getSuperRegs(); + for (unsigned j = 0, e = SR.size(); j != e; ++j) { + const CodeGenRegister *Super = SR[j]; + if (!Super->CoveredBySubRegs || Set.count(Super)) + continue; + // This new super-register is covered by its sub-registers. + bool AllSubsInSet = true; + const CodeGenRegister::SubRegMap &SRM = Super->getSubRegs(); + for (CodeGenRegister::SubRegMap::const_iterator I = SRM.begin(), + E = SRM.end(); I != E; ++I) + if (!Set.count(I->second)) { + AllSubsInSet = false; + break; + } + // All sub-registers in Set, add Super as well. + // We will visit Super later to recheck its super-registers. + if (AllSubsInSet) + Set.insert(Super); + } + } + + // Convert to BitVector. + BitVector BV(Registers.size() + 1); + for (unsigned i = 0, e = Set.size(); i != e; ++i) + BV.set(Set[i]->EnumValue); + return BV; +} diff --git a/utils/TableGen/CodeGenRegisters.h b/utils/TableGen/CodeGenRegisters.h index 4fc34b0..232a6e7 100644 --- a/utils/TableGen/CodeGenRegisters.h +++ b/utils/TableGen/CodeGenRegisters.h @@ -22,6 +22,7 @@ #include "llvm/ADT/BitVector.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SetVector.h" +#include "llvm/Support/ErrorHandling.h" #include <cstdlib> #include <map> #include <string> @@ -31,14 +32,69 @@ namespace llvm { class CodeGenRegBank; + /// CodeGenSubRegIndex - Represents a sub-register index. + class CodeGenSubRegIndex { + Record *const TheDef; + const unsigned EnumValue; + + public: + CodeGenSubRegIndex(Record *R, unsigned Enum); + + const std::string &getName() const; + std::string getNamespace() const; + std::string getQualifiedName() const; + + // Order CodeGenSubRegIndex pointers by EnumValue. + struct Less { + bool operator()(const CodeGenSubRegIndex *A, + const CodeGenSubRegIndex *B) const { + assert(A && B); + return A->EnumValue < B->EnumValue; + } + }; + + // Map of composite subreg indices. + typedef std::map<CodeGenSubRegIndex*, CodeGenSubRegIndex*, Less> CompMap; + + // Returns the subreg index that results from composing this with Idx. + // Returns NULL if this and Idx don't compose. + CodeGenSubRegIndex *compose(CodeGenSubRegIndex *Idx) const { + CompMap::const_iterator I = Composed.find(Idx); + return I == Composed.end() ? 0 : I->second; + } + + // Add a composite subreg index: this+A = B. + // Return a conflicting composite, or NULL + CodeGenSubRegIndex *addComposite(CodeGenSubRegIndex *A, + CodeGenSubRegIndex *B) { + std::pair<CompMap::iterator, bool> Ins = + Composed.insert(std::make_pair(A, B)); + return (Ins.second || Ins.first->second == B) ? 0 : Ins.first->second; + } + + // Update the composite maps of components specified in 'ComposedOf'. + void updateComponents(CodeGenRegBank&); + + // Clean out redundant composite mappings. + void cleanComposites(); + + // Return the map of composites. + const CompMap &getComposites() const { return Composed; } + + private: + CompMap Composed; + }; + /// CodeGenRegister - Represents a register definition. struct CodeGenRegister { Record *TheDef; unsigned EnumValue; unsigned CostPerUse; + bool CoveredBySubRegs; // Map SubRegIndex -> Register. - typedef std::map<Record*, CodeGenRegister*, LessRecord> SubRegMap; + typedef std::map<CodeGenSubRegIndex*, CodeGenRegister*, + CodeGenSubRegIndex::Less> SubRegMap; CodeGenRegister(Record *R, unsigned Enum); @@ -54,18 +110,37 @@ namespace llvm { } // Add sub-registers to OSet following a pre-order defined by the .td file. - void addSubRegsPreOrder(SetVector<CodeGenRegister*> &OSet) const; + void addSubRegsPreOrder(SetVector<const CodeGenRegister*> &OSet, + CodeGenRegBank&) const; // List of super-registers in topological order, small to large. - typedef std::vector<CodeGenRegister*> SuperRegList; + typedef std::vector<const CodeGenRegister*> SuperRegList; - // Get the list of super-registers. - // This is only valid after computeDerivedInfo has visited all registers. + // Get the list of super-registers. This is valid after getSubReg + // visits all registers during RegBank construction. const SuperRegList &getSuperRegs() const { assert(SubRegsComplete && "Must precompute sub-registers"); return SuperRegs; } + // List of register units in ascending order. + typedef SmallVector<unsigned, 16> RegUnitList; + + // Get the list of register units. + // This is only valid after getSubRegs() completes. + const RegUnitList &getRegUnits() const { return RegUnits; } + + // Inherit register units from subregisters. + // Return true if the RegUnits changed. + bool inheritRegUnits(CodeGenRegBank &RegBank); + + // Adopt a register unit for pressure tracking. + // A unit is adopted iff its unit number is >= NumNativeRegUnits. + void adoptRegUnit(unsigned RUID) { RegUnits.push_back(RUID); } + + // Get the sum of this register's register unit weights. + unsigned getWeight(const CodeGenRegBank &RegBank) const; + // Order CodeGenRegister pointers by EnumValue. struct Less { bool operator()(const CodeGenRegister *A, @@ -82,6 +157,7 @@ namespace llvm { bool SubRegsComplete; SubRegMap SubRegs; SuperRegList SuperRegs; + RegUnitList RegUnits; }; @@ -101,8 +177,17 @@ namespace llvm { // super-class. void inheritProperties(CodeGenRegBank&); - // Map SubRegIndex -> sub-class - DenseMap<Record*, CodeGenRegisterClass*> SubClassWithSubReg; + // Map SubRegIndex -> sub-class. This is the largest sub-class where all + // registers have a SubRegIndex sub-register. + DenseMap<CodeGenSubRegIndex*, CodeGenRegisterClass*> SubClassWithSubReg; + + // Map SubRegIndex -> set of super-reg classes. This is all register + // classes SuperRC such that: + // + // R:SubRegIndex in this RC for all R in SuperRC. + // + DenseMap<CodeGenSubRegIndex*, + SmallPtrSet<CodeGenRegisterClass*, 8> > SuperRegClasses; public: unsigned EnumValue; @@ -128,8 +213,7 @@ namespace llvm { MVT::SimpleValueType getValueTypeNum(unsigned VTNum) const { if (VTNum < VTs.size()) return VTs[VTNum]; - assert(0 && "VTNum greater than number of ValueTypes in RegClass!"); - abort(); + llvm_unreachable("VTNum greater than number of ValueTypes in RegClass!"); } // Return true if this this class contains the register. @@ -150,14 +234,26 @@ namespace llvm { // getSubClassWithSubReg - Returns the largest sub-class where all // registers have a SubIdx sub-register. - CodeGenRegisterClass *getSubClassWithSubReg(Record *SubIdx) const { + CodeGenRegisterClass* + getSubClassWithSubReg(CodeGenSubRegIndex *SubIdx) const { return SubClassWithSubReg.lookup(SubIdx); } - void setSubClassWithSubReg(Record *SubIdx, CodeGenRegisterClass *SubRC) { + void setSubClassWithSubReg(CodeGenSubRegIndex *SubIdx, + CodeGenRegisterClass *SubRC) { SubClassWithSubReg[SubIdx] = SubRC; } + // getSuperRegClasses - Returns a bit vector of all register classes + // containing only SubIdx super-registers of this class. + void getSuperRegClasses(CodeGenSubRegIndex *SubIdx, BitVector &Out) const; + + // addSuperRegClass - Add a class containing only SudIdx super-registers. + void addSuperRegClass(CodeGenSubRegIndex *SubIdx, + CodeGenRegisterClass *SuperRC) { + SuperRegClasses[SubIdx].insert(SuperRC); + } + // getSubClasses - Returns a constant BitVector of subclasses indexed by // EnumValue. // The SubClasses vector includs an entry for this class. @@ -183,6 +279,9 @@ namespace llvm { // getOrder(0). const CodeGenRegister::Set &getMembers() const { return Members; } + // Populate a unique sorted list of units from a register set. + void buildRegUnitSet(std::vector<unsigned> &RegUnits) const; + CodeGenRegisterClass(CodeGenRegBank&, Record *R); // A key representing the parts of a register class used for forming @@ -217,16 +316,34 @@ namespace llvm { static void computeSubClasses(CodeGenRegBank&); }; + // Each RegUnitSet is a sorted vector with a name. + struct RegUnitSet { + typedef std::vector<unsigned>::const_iterator iterator; + + std::string Name; + std::vector<unsigned> Units; + }; + // CodeGenRegBank - Represent a target's registers and the relations between // them. class CodeGenRegBank { RecordKeeper &Records; SetTheory Sets; - std::vector<Record*> SubRegIndices; + // SubRegIndices. + std::vector<CodeGenSubRegIndex*> SubRegIndices; + DenseMap<Record*, CodeGenSubRegIndex*> Def2SubRegIdx; unsigned NumNamedIndices; + + // Registers. std::vector<CodeGenRegister*> Registers; DenseMap<Record*, CodeGenRegister*> Def2Reg; + unsigned NumNativeRegUnits; + unsigned NumRegUnits; // # native + adopted register units. + + // Map each register unit to a weight (for register pressure). + // Includes native and adopted register units. + std::vector<unsigned> RegUnitWeights; // Register classes. std::vector<CodeGenRegisterClass*> RegClasses; @@ -234,16 +351,38 @@ namespace llvm { typedef std::map<CodeGenRegisterClass::Key, CodeGenRegisterClass*> RCKeyMap; RCKeyMap Key2RC; + // Remember each unique set of register units. Initially, this contains a + // unique set for each register class. Simliar sets are coalesced with + // pruneUnitSets and new supersets are inferred during computeRegUnitSets. + std::vector<RegUnitSet> RegUnitSets; + + // Map RegisterClass index to the index of the RegUnitSet that contains the + // class's units and any inferred RegUnit supersets. + std::vector<std::vector<unsigned> > RegClassUnitSets; + // Add RC to *2RC maps. void addToMaps(CodeGenRegisterClass*); + // Create a synthetic sub-class if it is missing. + CodeGenRegisterClass *getOrCreateSubClass(const CodeGenRegisterClass *RC, + const CodeGenRegister::Set *Membs, + StringRef Name); + // Infer missing register classes. void computeInferredRegisterClasses(); + void inferCommonSubClass(CodeGenRegisterClass *RC); + void inferSubClassWithSubReg(CodeGenRegisterClass *RC); + void inferMatchingSuperRegClass(CodeGenRegisterClass *RC, + unsigned FirstSubRegRC = 0); + + // Iteratively prune unit sets. + void pruneUnitSets(); + + // Compute a weight for each register unit created during getSubRegs. + void computeRegUnitWeights(); - // Composite SubRegIndex instances. - // Map (SubRegIndex, SubRegIndex) -> SubRegIndex. - typedef DenseMap<std::pair<Record*, Record*>, Record*> CompositeMap; - CompositeMap Composite; + // Create a RegUnitSet for each RegClass and infer superclasses. + void computeRegUnitSets(); // Populate the Composite map from sub-register relationships. void computeComposites(); @@ -256,20 +395,44 @@ namespace llvm { // Sub-register indices. The first NumNamedIndices are defined by the user // in the .td files. The rest are synthesized such that all sub-registers // have a unique name. - const std::vector<Record*> &getSubRegIndices() { return SubRegIndices; } + ArrayRef<CodeGenSubRegIndex*> getSubRegIndices() { return SubRegIndices; } unsigned getNumNamedIndices() { return NumNamedIndices; } - // Map a SubRegIndex Record to its enum value. - unsigned getSubRegIndexNo(Record *idx); + // Find a SubRegIndex form its Record def. + CodeGenSubRegIndex *getSubRegIdx(Record*); // Find or create a sub-register index representing the A+B composition. - Record *getCompositeSubRegIndex(Record *A, Record *B, bool create = false); + CodeGenSubRegIndex *getCompositeSubRegIndex(CodeGenSubRegIndex *A, + CodeGenSubRegIndex *B); const std::vector<CodeGenRegister*> &getRegisters() { return Registers; } // Find a register from its Record def. CodeGenRegister *getReg(Record*); + // Get a Register's index into the Registers array. + unsigned getRegIndex(const CodeGenRegister *Reg) const { + return Reg->EnumValue - 1; + } + + // Create a new non-native register unit that can be adopted by a register + // to increase its pressure. Note that NumNativeRegUnits is not increased. + unsigned newRegUnit(unsigned Weight) { + if (!RegUnitWeights.empty()) { + assert(Weight && "should only add allocatable units"); + RegUnitWeights.resize(NumRegUnits+1); + RegUnitWeights[NumRegUnits] = Weight; + } + return NumRegUnits++; + } + + // Native units are the singular unit of a leaf register. Register aliasing + // is completely characterized by native units. Adopted units exist to give + // register additional weight but don't affect aliasing. + bool isNativeUnit(unsigned RUID) { + return RUID < NumNativeRegUnits; + } + ArrayRef<CodeGenRegisterClass*> getRegClasses() const { return RegClasses; } @@ -284,6 +447,41 @@ namespace llvm { /// return the superclass. Otherwise return null. const CodeGenRegisterClass* getRegClassForRegister(Record *R); + // Get a register unit's weight. Zero for unallocatable registers. + unsigned getRegUnitWeight(unsigned RUID) const { + return RegUnitWeights[RUID]; + } + + // Get the sum of unit weights. + unsigned getRegUnitSetWeight(const std::vector<unsigned> &Units) const { + unsigned Weight = 0; + for (std::vector<unsigned>::const_iterator + I = Units.begin(), E = Units.end(); I != E; ++I) + Weight += getRegUnitWeight(*I); + return Weight; + } + + // Increase a RegUnitWeight. + void increaseRegUnitWeight(unsigned RUID, unsigned Inc) { + RegUnitWeights[RUID] += Inc; + } + + // Get the number of register pressure dimensions. + unsigned getNumRegPressureSets() const { return RegUnitSets.size(); } + + // Get a set of register unit IDs for a given dimension of pressure. + RegUnitSet getRegPressureSet(unsigned Idx) const { + return RegUnitSets[Idx]; + } + + // Get a list of pressure set IDs for a register class. Liveness of a + // register in this class impacts each pressure set in this list by the + // weight of the register. An exact solution requires all registers in a + // class to have the same class, but it is not strictly guaranteed. + ArrayRef<unsigned> getRCPressureSetIDs(unsigned RCIdx) const { + return RegClassUnitSets[RCIdx]; + } + // Computed derived records such as missing sub-register indices. void computeDerivedInfo(); @@ -295,6 +493,15 @@ namespace llvm { // If R1 is a sub-register of R2, Map[R1] is a subset of Map[R2]. void computeOverlaps(std::map<const CodeGenRegister*, CodeGenRegister::Set> &Map); + + // Compute the set of registers completely covered by the registers in Regs. + // The returned BitVector will have a bit set for each register in Regs, + // all sub-registers, and all super-registers that are covered by the + // registers in Regs. + // + // This is used to compute the mask of call-preserved registers from a list + // of callee-saves. + BitVector computeCoveredRegisters(ArrayRef<Record*> Regs); }; } diff --git a/utils/TableGen/CodeGenTarget.cpp b/utils/TableGen/CodeGenTarget.cpp index 4a7bad7..cf67935 100644 --- a/utils/TableGen/CodeGenTarget.cpp +++ b/utils/TableGen/CodeGenTarget.cpp @@ -58,6 +58,7 @@ std::string llvm::getEnumName(MVT::SimpleValueType T) { case MVT::iAny: return "MVT::iAny"; case MVT::fAny: return "MVT::fAny"; case MVT::vAny: return "MVT::vAny"; + case MVT::f16: return "MVT::f16"; case MVT::f32: return "MVT::f32"; case MVT::f64: return "MVT::f64"; case MVT::f80: return "MVT::f80"; @@ -82,6 +83,7 @@ std::string llvm::getEnumName(MVT::SimpleValueType T) { case MVT::v2i64: return "MVT::v2i64"; case MVT::v4i64: return "MVT::v4i64"; case MVT::v8i64: return "MVT::v8i64"; + case MVT::v2f16: return "MVT::v2f16"; case MVT::v2f32: return "MVT::v2f32"; case MVT::v4f32: return "MVT::v4f32"; case MVT::v8f32: return "MVT::v8f32"; @@ -90,8 +92,8 @@ std::string llvm::getEnumName(MVT::SimpleValueType T) { case MVT::Metadata: return "MVT::Metadata"; case MVT::iPTR: return "MVT::iPTR"; case MVT::iPTRAny: return "MVT::iPTRAny"; - case MVT::untyped: return "MVT::untyped"; - default: assert(0 && "ILLEGAL VALUE TYPE!"); return ""; + case MVT::Untyped: return "MVT::Untyped"; + default: llvm_unreachable("ILLEGAL VALUE TYPE!"); } } @@ -149,6 +151,26 @@ Record *CodeGenTarget::getAsmParser() const { return LI[AsmParserNum]; } +/// getAsmParserVariant - Return the AssmblyParserVariant definition for +/// this target. +/// +Record *CodeGenTarget::getAsmParserVariant(unsigned i) const { + std::vector<Record*> LI = + TargetRec->getValueAsListOfDefs("AssemblyParserVariants"); + if (i >= LI.size()) + throw "Target does not have an AsmParserVariant #" + utostr(i) + "!"; + return LI[i]; +} + +/// getAsmParserVariantCount - Return the AssmblyParserVariant definition +/// available for this target. +/// +unsigned CodeGenTarget::getAsmParserVariantCount() const { + std::vector<Record*> LI = + TargetRec->getValueAsListOfDefs("AssemblyParserVariants"); + return LI.size(); +} + /// getAsmWriter - Return the AssemblyWriter definition for this target. /// Record *CodeGenTarget::getAsmWriter() const { @@ -267,6 +289,7 @@ void CodeGenTarget::ComputeInstrsByEnum() const { "DBG_VALUE", "REG_SEQUENCE", "COPY", + "BUNDLE", 0 }; const DenseMap<const Record*, CodeGenInstruction*> &Insts = getInstructions(); @@ -492,7 +515,7 @@ CodeGenIntrinsic::CodeGenIntrinsic(Record *R) { unsigned ArgNo = Property->getValueAsInt("ArgNo"); ArgumentAttributes.push_back(std::make_pair(ArgNo, NoCapture)); } else - assert(0 && "Unknown property!"); + llvm_unreachable("Unknown property!"); } // Sort the argument attributes for later benefit. diff --git a/utils/TableGen/CodeGenTarget.h b/utils/TableGen/CodeGenTarget.h index 730216c..85463da 100644 --- a/utils/TableGen/CodeGenTarget.h +++ b/utils/TableGen/CodeGenTarget.h @@ -91,6 +91,16 @@ public: /// Record *getAsmParser() const; + /// getAsmParserVariant - Return the AssmblyParserVariant definition for + /// this target. + /// + Record *getAsmParserVariant(unsigned i) const; + + /// getAsmParserVariantCount - Return the AssmblyParserVariant definition + /// available for this target. + /// + unsigned getAsmParserVariantCount() const; + /// getAsmWriter - Return the AssemblyWriter definition for this target. /// Record *getAsmWriter() const; diff --git a/utils/TableGen/DAGISelMatcher.cpp b/utils/TableGen/DAGISelMatcher.cpp index 1367e8d..bd77907 100644 --- a/utils/TableGen/DAGISelMatcher.cpp +++ b/utils/TableGen/DAGISelMatcher.cpp @@ -15,6 +15,8 @@ #include "llvm/ADT/StringExtras.h" using namespace llvm; +void Matcher::anchor() { } + void Matcher::dump() const { print(errs(), 0); } @@ -324,6 +326,10 @@ unsigned EmitNodeMatcherCommon::getHashImpl() const { } +void EmitNodeMatcher::anchor() { } + +void MorphNodeToMatcher::anchor() { } + unsigned MarkGlueResultsMatcher::getHashImpl() const { return HashUnsigneds(GlueResultNodes.begin(), GlueResultNodes.end()); } diff --git a/utils/TableGen/DAGISelMatcher.h b/utils/TableGen/DAGISelMatcher.h index dcb8da7..99ebf98 100644 --- a/utils/TableGen/DAGISelMatcher.h +++ b/utils/TableGen/DAGISelMatcher.h @@ -41,6 +41,7 @@ class Matcher { // The next matcher node that is executed after this one. Null if this is the // last stage of a match. OwningPtr<Matcher> Next; + virtual void anchor(); public: enum KindTy { // Matcher state manipulation. @@ -1011,6 +1012,7 @@ private: /// EmitNodeMatcher - This signals a successful match and generates a node. class EmitNodeMatcher : public EmitNodeMatcherCommon { + virtual void anchor(); unsigned FirstResultSlot; public: EmitNodeMatcher(const std::string &opcodeName, @@ -1033,6 +1035,7 @@ public: }; class MorphNodeToMatcher : public EmitNodeMatcherCommon { + virtual void anchor(); const PatternToMatch &Pattern; public: MorphNodeToMatcher(const std::string &opcodeName, diff --git a/utils/TableGen/DAGISelMatcherEmitter.cpp b/utils/TableGen/DAGISelMatcherEmitter.cpp index 3b65b2a..bd425a9 100644 --- a/utils/TableGen/DAGISelMatcherEmitter.cpp +++ b/utils/TableGen/DAGISelMatcherEmitter.cpp @@ -573,8 +573,7 @@ EmitMatcher(const Matcher *N, unsigned Indent, unsigned CurrentIdx, return 2 + NumResultBytes; } } - assert(0 && "Unreachable"); - return 0; + llvm_unreachable("Unreachable"); } /// EmitMatcherList - Emit the bytes for the specified matcher subtree. @@ -601,7 +600,7 @@ void MatcherTableEmitter::EmitPredicateFunctions(formatted_raw_ostream &OS) { if (!PatternPredicates.empty()) { OS << "bool CheckPatternPredicate(unsigned PredNo) const {\n"; OS << " switch (PredNo) {\n"; - OS << " default: assert(0 && \"Invalid predicate in table?\");\n"; + OS << " default: llvm_unreachable(\"Invalid predicate in table?\");\n"; for (unsigned i = 0, e = PatternPredicates.size(); i != e; ++i) OS << " case " << i << ": return " << PatternPredicates[i] << ";\n"; OS << " }\n"; @@ -619,7 +618,7 @@ void MatcherTableEmitter::EmitPredicateFunctions(formatted_raw_ostream &OS) { if (!NodePredicates.empty()) { OS << "bool CheckNodePredicate(SDNode *Node, unsigned PredNo) const {\n"; OS << " switch (PredNo) {\n"; - OS << " default: assert(0 && \"Invalid predicate in table?\");\n"; + OS << " default: llvm_unreachable(\"Invalid predicate in table?\");\n"; for (unsigned i = 0, e = NodePredicates.size(); i != e; ++i) { // Emit the predicate code corresponding to this pattern. TreePredicateFn PredFn = NodePredicates[i]; @@ -641,7 +640,7 @@ void MatcherTableEmitter::EmitPredicateFunctions(formatted_raw_ostream &OS) { OS << " SmallVectorImpl<std::pair<SDValue, SDNode*> > &Result) {\n"; OS << " unsigned NextRes = Result.size();\n"; OS << " switch (PatternNo) {\n"; - OS << " default: assert(0 && \"Invalid pattern # in table?\");\n"; + OS << " default: llvm_unreachable(\"Invalid pattern # in table?\");\n"; for (unsigned i = 0, e = ComplexPatterns.size(); i != e; ++i) { const ComplexPattern &P = *ComplexPatterns[i]; unsigned NumOps = P.getNumOperands(); @@ -679,7 +678,7 @@ void MatcherTableEmitter::EmitPredicateFunctions(formatted_raw_ostream &OS) { if (!NodeXForms.empty()) { OS << "SDValue RunSDNodeXForm(SDValue V, unsigned XFormNo) {\n"; OS << " switch (XFormNo) {\n"; - OS << " default: assert(0 && \"Invalid xform # in table?\");\n"; + OS << " default: llvm_unreachable(\"Invalid xform # in table?\");\n"; // FIXME: The node xform could take SDValue's instead of SDNode*'s. for (unsigned i = 0, e = NodeXForms.size(); i != e; ++i) { diff --git a/utils/TableGen/DAGISelMatcherGen.cpp b/utils/TableGen/DAGISelMatcherGen.cpp index 49ad956..2ac7b87 100644 --- a/utils/TableGen/DAGISelMatcherGen.cpp +++ b/utils/TableGen/DAGISelMatcherGen.cpp @@ -217,7 +217,7 @@ void MatcherGen::EmitLeafMatchCode(const TreePatternNode *N) { DefInit *DI = dynamic_cast<DefInit*>(N->getLeafValue()); if (DI == 0) { - errs() << "Unknown leaf kind: " << *DI << "\n"; + errs() << "Unknown leaf kind: " << *N << "\n"; abort(); } diff --git a/utils/TableGen/DFAPacketizerEmitter.cpp b/utils/TableGen/DFAPacketizerEmitter.cpp new file mode 100644 index 0000000..4abf54e --- /dev/null +++ b/utils/TableGen/DFAPacketizerEmitter.cpp @@ -0,0 +1,512 @@ +//===- DFAPacketizerEmitter.cpp - Packetization DFA for a VLIW machine-----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class parses the Schedule.td file and produces an API that can be used +// to reason about whether an instruction can be added to a packet on a VLIW +// architecture. The class internally generates a deterministic finite +// automaton (DFA) that models all possible mappings of machine instructions +// to functional units as instructions are added to a packet. +// +//===----------------------------------------------------------------------===// + +#include "llvm/TableGen/Record.h" +#include "CodeGenTarget.h" +#include "DFAPacketizerEmitter.h" +#include <list> + +using namespace llvm; + +// +// +// State represents the usage of machine resources if the packet contains +// a set of instruction classes. +// +// Specifically, currentState is a set of bit-masks. +// The nth bit in a bit-mask indicates whether the nth resource is being used +// by this state. The set of bit-masks in a state represent the different +// possible outcomes of transitioning to this state. +// For example: consider a two resource architecture: resource L and resource M +// with three instruction classes: L, M, and L_or_M. +// From the initial state (currentState = 0x00), if we add instruction class +// L_or_M we will transition to a state with currentState = [0x01, 0x10]. This +// represents the possible resource states that can result from adding a L_or_M +// instruction +// +// Another way of thinking about this transition is we are mapping a NDFA with +// two states [0x01] and [0x10] into a DFA with a single state [0x01, 0x10]. +// +// +namespace { +class State { + public: + static int currentStateNum; + int stateNum; + bool isInitial; + std::set<unsigned> stateInfo; + + State(); + State(const State &S); + + // + // canAddInsnClass - Returns true if an instruction of type InsnClass is a + // valid transition from this state, i.e., can an instruction of type InsnClass + // be added to the packet represented by this state. + // + // PossibleStates is the set of valid resource states that ensue from valid + // transitions. + // + bool canAddInsnClass(unsigned InsnClass, std::set<unsigned> &PossibleStates); +}; +} // End anonymous namespace. + + +namespace { +struct Transition { + public: + static int currentTransitionNum; + int transitionNum; + State *from; + unsigned input; + State *to; + + Transition(State *from_, unsigned input_, State *to_); +}; +} // End anonymous namespace. + + +// +// Comparators to keep set of states sorted. +// +namespace { +struct ltState { + bool operator()(const State *s1, const State *s2) const; +}; +} // End anonymous namespace. + + +// +// class DFA: deterministic finite automaton for processor resource tracking. +// +namespace { +class DFA { +public: + DFA(); + + // Set of states. Need to keep this sorted to emit the transition table. + std::set<State*, ltState> states; + + // Map from a state to the list of transitions with that state as source. + std::map<State*, SmallVector<Transition*, 16>, ltState> stateTransitions; + State *currentState; + + // Highest valued Input seen. + unsigned LargestInput; + + // + // Modify the DFA. + // + void initialize(); + void addState(State *); + void addTransition(Transition *); + + // + // getTransition - Return the state when a transition is made from + // State From with Input I. If a transition is not found, return NULL. + // + State *getTransition(State *, unsigned); + + // + // isValidTransition: Predicate that checks if there is a valid transition + // from state From on input InsnClass. + // + bool isValidTransition(State *From, unsigned InsnClass); + + // + // writeTable: Print out a table representing the DFA. + // + void writeTableAndAPI(raw_ostream &OS, const std::string &ClassName); +}; +} // End anonymous namespace. + + +// +// Constructors for State, Transition, and DFA +// +State::State() : + stateNum(currentStateNum++), isInitial(false) {} + + +State::State(const State &S) : + stateNum(currentStateNum++), isInitial(S.isInitial), + stateInfo(S.stateInfo) {} + + +Transition::Transition(State *from_, unsigned input_, State *to_) : + transitionNum(currentTransitionNum++), from(from_), input(input_), + to(to_) {} + + +DFA::DFA() : + LargestInput(0) {} + + +bool ltState::operator()(const State *s1, const State *s2) const { + return (s1->stateNum < s2->stateNum); +} + + +// +// canAddInsnClass - Returns true if an instruction of type InsnClass is a +// valid transition from this state i.e., can an instruction of type InsnClass +// be added to the packet represented by this state. +// +// PossibleStates is the set of valid resource states that ensue from valid +// transitions. +// +bool State::canAddInsnClass(unsigned InsnClass, + std::set<unsigned> &PossibleStates) { + // + // Iterate over all resource states in currentState. + // + bool AddedState = false; + + for (std::set<unsigned>::iterator SI = stateInfo.begin(); + SI != stateInfo.end(); ++SI) { + unsigned thisState = *SI; + + // + // Iterate over all possible resources used in InsnClass. + // For ex: for InsnClass = 0x11, all resources = {0x01, 0x10}. + // + + DenseSet<unsigned> VisitedResourceStates; + for (unsigned int j = 0; j < sizeof(InsnClass) * 8; ++j) { + if ((0x1 << j) & InsnClass) { + // + // For each possible resource used in InsnClass, generate the + // resource state if that resource was used. + // + unsigned ResultingResourceState = thisState | (0x1 << j); + // + // Check if the resulting resource state can be accommodated in this + // packet. + // We compute ResultingResourceState OR thisState. + // If the result of the OR is different than thisState, it implies + // that there is at least one resource that can be used to schedule + // InsnClass in the current packet. + // Insert ResultingResourceState into PossibleStates only if we haven't + // processed ResultingResourceState before. + // + if ((ResultingResourceState != thisState) && + (VisitedResourceStates.count(ResultingResourceState) == 0)) { + VisitedResourceStates.insert(ResultingResourceState); + PossibleStates.insert(ResultingResourceState); + AddedState = true; + } + } + } + } + + return AddedState; +} + + +void DFA::initialize() { + currentState->isInitial = true; +} + + +void DFA::addState(State *S) { + assert(!states.count(S) && "State already exists"); + states.insert(S); +} + + +void DFA::addTransition(Transition *T) { + // Update LargestInput. + if (T->input > LargestInput) + LargestInput = T->input; + + // Add the new transition. + stateTransitions[T->from].push_back(T); +} + + +// +// getTransition - Return the state when a transition is made from +// State From with Input I. If a transition is not found, return NULL. +// +State *DFA::getTransition(State *From, unsigned I) { + // Do we have a transition from state From? + if (!stateTransitions.count(From)) + return NULL; + + // Do we have a transition from state From with Input I? + for (SmallVector<Transition*, 16>::iterator VI = + stateTransitions[From].begin(); + VI != stateTransitions[From].end(); ++VI) + if ((*VI)->input == I) + return (*VI)->to; + + return NULL; +} + + +bool DFA::isValidTransition(State *From, unsigned InsnClass) { + return (getTransition(From, InsnClass) != NULL); +} + + +int State::currentStateNum = 0; +int Transition::currentTransitionNum = 0; + +DFAGen::DFAGen(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. +// +// Format: +// DFAStateInputTable[][2] = pairs of <Input, Transition> for all valid +// transitions. +// DFAStateEntryTable[i] = Index of the first entry in DFAStateInputTable for +// the ith state. +// +// +void DFA::writeTableAndAPI(raw_ostream &OS, const std::string &TargetName) { + std::set<State*, ltState>::iterator SI = states.begin(); + // This table provides a map to the beginning of the transitions for State s + // in DFAStateInputTable. + std::vector<int> StateEntry(states.size()); + + OS << "namespace llvm {\n\n"; + OS << "const int " << TargetName << "DFAStateInputTable[][2] = {\n"; + + // Tracks the total valid transitions encountered so far. It is used + // to construct the StateEntry table. + int ValidTransitions = 0; + for (unsigned i = 0; i < states.size(); ++i, ++SI) { + StateEntry[i] = ValidTransitions; + for (unsigned j = 0; j <= LargestInput; ++j) { + assert (((*SI)->stateNum == (int) i) && "Mismatch in state numbers"); + if (!isValidTransition(*SI, j)) + continue; + + OS << "{" << j << ", " + << getTransition(*SI, j)->stateNum + << "}, "; + ++ValidTransitions; + } + + // If there are no valid transitions from this stage, we need a sentinel + // transition. + if (ValidTransitions == StateEntry[i]) { + OS << "{-1, -1},"; + ++ValidTransitions; + } + + OS << "\n"; + } + OS << "};\n\n"; + OS << "const unsigned int " << TargetName << "DFAStateEntryTable[] = {\n"; + + // Multiply i by 2 since each entry in DFAStateInputTable is a set of + // two numbers. + for (unsigned i = 0; i < states.size(); ++i) + OS << StateEntry[i] << ", "; + + OS << "\n};\n"; + OS << "} // namespace\n"; + + + // + // Emit DFA Packetizer tables if the target is a VLIW machine. + // + std::string SubTargetClassName = TargetName + "GenSubtargetInfo"; + OS << "\n" << "#include \"llvm/CodeGen/DFAPacketizer.h\"\n"; + OS << "namespace llvm {\n"; + OS << "DFAPacketizer *" << SubTargetClassName << "::" + << "createDFAPacketizer(const InstrItineraryData *IID) const {\n" + << " return new DFAPacketizer(IID, " << TargetName + << "DFAStateInputTable, " << TargetName << "DFAStateEntryTable);\n}\n\n"; + OS << "} // End llvm namespace \n"; +} + + +// +// collectAllInsnClasses - Populate allInsnClasses which is a set of units +// used in each stage. +// +void DFAGen::collectAllInsnClasses(const std::string &Name, + Record *ItinData, + unsigned &NStages, + raw_ostream &OS) { + // Collect processor itineraries. + std::vector<Record*> ProcItinList = + Records.getAllDerivedDefinitions("ProcessorItineraries"); + + // If just no itinerary then don't bother. + if (ProcItinList.size() < 2) + return; + std::map<std::string, unsigned> NameToBitsMap; + + // Parse functional units for all the itineraries. + for (unsigned i = 0, N = ProcItinList.size(); i < N; ++i) { + Record *Proc = ProcItinList[i]; + std::vector<Record*> FUs = Proc->getValueAsListOfDefs("FU"); + + // Convert macros to bits for each stage. + for (unsigned i = 0, N = FUs.size(); i < N; ++i) + NameToBitsMap[FUs[i]->getName()] = (unsigned) (1U << i); + } + + const std::vector<Record*> &StageList = + ItinData->getValueAsListOfDefs("Stages"); + + // The number of stages. + NStages = StageList.size(); + + // For each unit. + unsigned UnitBitValue = 0; + + // Compute the bitwise or of each unit used in this stage. + for (unsigned i = 0; i < NStages; ++i) { + const Record *Stage = StageList[i]; + + // Get unit list. + const std::vector<Record*> &UnitList = + Stage->getValueAsListOfDefs("Units"); + + for (unsigned j = 0, M = UnitList.size(); j < M; ++j) { + // Conduct bitwise or. + std::string UnitName = UnitList[j]->getName(); + assert(NameToBitsMap.count(UnitName)); + UnitBitValue |= NameToBitsMap[UnitName]; + } + + if (UnitBitValue != 0) + allInsnClasses.insert(UnitBitValue); + } +} + + +// +// Run the worklist algorithm to generate the DFA. +// +void DFAGen::run(raw_ostream &OS) { + EmitSourceFileHeader("Target DFA Packetizer Tables", OS); + + // Collect processor iteraries. + std::vector<Record*> ProcItinList = + Records.getAllDerivedDefinitions("ProcessorItineraries"); + + // + // Collect the instruction classes. + // + for (unsigned i = 0, N = ProcItinList.size(); i < N; i++) { + Record *Proc = ProcItinList[i]; + + // Get processor itinerary name. + const std::string &Name = Proc->getName(); + + // Skip default. + if (Name == "NoItineraries") + continue; + + // Sanity check for at least one instruction itinerary class. + unsigned NItinClasses = + Records.getAllDerivedDefinitions("InstrItinClass").size(); + if (NItinClasses == 0) + return; + + // Get itinerary data list. + std::vector<Record*> ItinDataList = Proc->getValueAsListOfDefs("IID"); + + // Collect instruction classes for all itinerary data. + for (unsigned j = 0, M = ItinDataList.size(); j < M; j++) { + Record *ItinData = ItinDataList[j]; + unsigned NStages; + collectAllInsnClasses(Name, ItinData, NStages, OS); + } + } + + + // + // Run a worklist algorithm to generate the DFA. + // + DFA D; + State *Initial = new State; + Initial->isInitial = true; + Initial->stateInfo.insert(0x0); + D.addState(Initial); + SmallVector<State*, 32> WorkList; + std::map<std::set<unsigned>, State*> Visited; + + WorkList.push_back(Initial); + + // + // Worklist algorithm to create a DFA for processor resource tracking. + // C = {set of InsnClasses} + // Begin with initial node in worklist. Initial node does not have + // any consumed resources, + // ResourceState = 0x0 + // Visited = {} + // While worklist != empty + // S = first element of worklist + // For every instruction class C + // if we can accommodate C in S: + // S' = state with resource states = {S Union C} + // Add a new transition: S x C -> S' + // If S' is not in Visited: + // Add S' to worklist + // Add S' to Visited + // + while (!WorkList.empty()) { + State *current = WorkList.pop_back_val(); + for (DenseSet<unsigned>::iterator CI = allInsnClasses.begin(), + CE = allInsnClasses.end(); CI != CE; ++CI) { + unsigned InsnClass = *CI; + + std::set<unsigned> NewStateResources; + // + // If we haven't already created a transition for this input + // and the state can accommodate this InsnClass, create a transition. + // + if (!D.getTransition(current, InsnClass) && + current->canAddInsnClass(InsnClass, NewStateResources)) { + State *NewState = NULL; + + // + // If we have seen this state before, then do not create a new state. + // + // + std::map<std::set<unsigned>, State*>::iterator VI; + if ((VI = Visited.find(NewStateResources)) != Visited.end()) + NewState = VI->second; + else { + NewState = new State; + NewState->stateInfo = NewStateResources; + D.addState(NewState); + Visited[NewStateResources] = NewState; + WorkList.push_back(NewState); + } + + Transition *NewTransition = new Transition(current, InsnClass, + NewState); + D.addTransition(NewTransition); + } + } + } + + // Print out the table. + D.writeTableAndAPI(OS, TargetName); +} diff --git a/utils/TableGen/DFAPacketizerEmitter.h b/utils/TableGen/DFAPacketizerEmitter.h new file mode 100644 index 0000000..1727150 --- /dev/null +++ b/utils/TableGen/DFAPacketizerEmitter.h @@ -0,0 +1,52 @@ +//===- DFAPacketizerEmitter.h - Packetization DFA for a VLIW machine-------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class parses the Schedule.td file and produces an API that can be used +// to reason about whether an instruction can be added to a packet on a VLIW +// architecture. The class internally generates a deterministic finite +// automaton (DFA) that models all possible mappings of machine instructions +// to functional units as instructions are added to a packet. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/DenseSet.h" +#include "llvm/TableGen/TableGenBackend.h" +#include <map> +#include <string> + +namespace llvm { +// +// class DFAGen: class that generates and prints out the DFA for resource +// tracking. +// +class DFAGen : public TableGenBackend { +private: + std::string TargetName; + // + // allInsnClasses is the set of all possible resources consumed by an + // InstrStage. + // + DenseSet<unsigned> allInsnClasses; + RecordKeeper &Records; + +public: + DFAGen(RecordKeeper &R); + + // + // collectAllInsnClasses: Populate allInsnClasses which is a set of units + // used in each stage. + // + void collectAllInsnClasses(const std::string &Name, + Record *ItinData, + unsigned &NStages, + raw_ostream &OS); + + void run(raw_ostream &OS); +}; +} diff --git a/utils/TableGen/DisassemblerEmitter.cpp b/utils/TableGen/DisassemblerEmitter.cpp index ff314e9..4650197 100644 --- a/utils/TableGen/DisassemblerEmitter.cpp +++ b/utils/TableGen/DisassemblerEmitter.cpp @@ -11,7 +11,6 @@ #include "CodeGenTarget.h" #include "X86DisassemblerTables.h" #include "X86RecognizableInstr.h" -#include "ARMDecoderEmitter.h" #include "FixedLenDecoderEmitter.h" #include "llvm/TableGen/Error.h" #include "llvm/TableGen/Record.h" diff --git a/utils/TableGen/EDEmitter.cpp b/utils/TableGen/EDEmitter.cpp index abef70e..3809a45 100644 --- a/utils/TableGen/EDEmitter.cpp +++ b/utils/TableGen/EDEmitter.cpp @@ -287,6 +287,7 @@ static int X86TypeFromOpName(LiteralConstantEmitter *type, IMM("i64i8imm"); IMM("i64i32imm"); IMM("SSECC"); + IMM("AVXCC"); // all R, I, R, I, R MEM("i8mem"); @@ -519,6 +520,8 @@ static void X86ExtractSemantics( // ignore (doesn't go anywhere we know about) } else if (name.find("VMCALL") != name.npos) { // ignore (rather different semantics than a regular call) + } else if (name.find("VMMCALL") != name.npos) { + // ignore (rather different semantics than a regular call) } else if (name.find("FAR") != name.npos && name.find("i") != name.npos) { CALL("off"); } else { @@ -567,12 +570,23 @@ static int ARMFlagFromOpName(LiteralConstantEmitter *type, REG("DPR"); REG("DPR_VFP2"); REG("DPR_8"); + REG("DPair"); REG("SPR"); REG("QPR"); REG("QQPR"); REG("QQQQPR"); + REG("VecListOneD"); + REG("VecListDPair"); + REG("VecListDPairSpaced"); + REG("VecListThreeD"); + REG("VecListFourD"); + REG("VecListOneDAllLanes"); + REG("VecListDPairAllLanes"); + REG("VecListDPairSpacedAllLanes"); IMM("i32imm"); + IMM("fbits16"); + IMM("fbits32"); IMM("i32imm_hilo16"); IMM("bf_inv_mask_imm"); IMM("lsb_pos_imm"); @@ -597,6 +611,20 @@ static int ARMFlagFromOpName(LiteralConstantEmitter *type, IMM("imm1_16"); IMM("imm1_32"); IMM("nModImm"); + IMM("nImmSplatI8"); + IMM("nImmSplatI16"); + IMM("nImmSplatI32"); + IMM("nImmSplatI64"); + IMM("nImmVMOVI32"); + IMM("nImmVMOVF32"); + IMM("imm8"); + IMM("imm16"); + IMM("imm32"); + IMM("imm1_7"); + IMM("imm1_15"); + IMM("imm1_31"); + IMM("imm0_1"); + IMM("imm0_3"); IMM("imm0_7"); IMM("imm0_15"); IMM("imm0_255"); @@ -735,7 +763,7 @@ static void ARMPopulateOperands( errs() << "Operand type: " << rec.getName() << '\n'; errs() << "Operand name: " << operandInfo.Name << '\n'; errs() << "Instruction name: " << inst.TheDef->getName() << '\n'; - llvm_unreachable("Unhandled type"); + throw("Unhandled type in EDEmitter"); } } } @@ -956,11 +984,7 @@ void EDEmitter::run(raw_ostream &o) { emitCommonEnums(o, i); - o << "namespace {\n"; - - o << "llvm::EDInstInfo instInfo" << target.getName().c_str() << "[] = "; + o << "static const llvm::EDInstInfo instInfo" << target.getName() << "[] = "; infoArray.emit(o, i); o << ";" << "\n"; - - o << "}\n"; } diff --git a/utils/TableGen/FastISelEmitter.cpp b/utils/TableGen/FastISelEmitter.cpp index 9fdc2e3..e8dad77 100644 --- a/utils/TableGen/FastISelEmitter.cpp +++ b/utils/TableGen/FastISelEmitter.cpp @@ -21,7 +21,6 @@ #include "llvm/TableGen/Error.h" #include "llvm/TableGen/Record.h" #include "llvm/ADT/SmallString.h" -#include "llvm/ADT/VectorExtras.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" using namespace llvm; @@ -280,7 +279,7 @@ struct OperandsSignature { } else if (Operands[i].isImm()) { OS << "uint64_t imm" << i; } else if (Operands[i].isFP()) { - OS << "ConstantFP *f" << i; + OS << "const ConstantFP *f" << i; } else { llvm_unreachable("Unknown operand kind!"); } diff --git a/utils/TableGen/FixedLenDecoderEmitter.cpp b/utils/TableGen/FixedLenDecoderEmitter.cpp index 02b966a..9b676f2 100644 --- a/utils/TableGen/FixedLenDecoderEmitter.cpp +++ b/utils/TableGen/FixedLenDecoderEmitter.cpp @@ -17,6 +17,7 @@ #include "FixedLenDecoderEmitter.h" #include "CodeGenTarget.h" #include "llvm/TableGen/Record.h" +#include "llvm/ADT/APInt.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" @@ -48,7 +49,7 @@ static bool ValueNotSet(bit_value_t V) { static int Value(bit_value_t V) { return ValueNotSet(V) ? -1 : (V == BIT_FALSE ? 0 : 1); } -static bit_value_t bitFromBits(BitsInit &bits, unsigned index) { +static bit_value_t bitFromBits(const BitsInit &bits, unsigned index) { if (BitInit *bit = dynamic_cast<BitInit*>(bits.getBit(index))) return bit->getValue() ? BIT_TRUE : BIT_FALSE; @@ -56,7 +57,7 @@ static bit_value_t bitFromBits(BitsInit &bits, unsigned index) { return BIT_UNSET; } // Prints the bit value for each position. -static void dumpBits(raw_ostream &o, BitsInit &bits) { +static void dumpBits(raw_ostream &o, const BitsInit &bits) { unsigned index; for (index = bits.getNumBits(); index > 0; index--) { @@ -71,7 +72,7 @@ static void dumpBits(raw_ostream &o, BitsInit &bits) { o << "_"; break; default: - assert(0 && "unexpected return value from bitFromBits"); + llvm_unreachable("unexpected return value from bitFromBits"); } } } @@ -125,7 +126,7 @@ typedef std::vector<bit_value_t> insn_t; /// version and return the Opcode since the two have the same Asm format string. class Filter { protected: - FilterChooser *Owner; // points to the FilterChooser who owns this filter + const FilterChooser *Owner;// points to the FilterChooser who owns this filter unsigned StartBit; // the starting bit position unsigned NumBits; // number of bits to filter bool Mixed; // a mixed region contains both set and unset bits @@ -137,7 +138,7 @@ protected: std::vector<unsigned> VariableInstructions; // Map of well-known segment value to its delegate. - std::map<unsigned, FilterChooser*> FilterChooserMap; + std::map<unsigned, const FilterChooser*> FilterChooserMap; // Number of instructions which fall under FilteredInstructions category. unsigned NumFiltered; @@ -145,19 +146,15 @@ protected: // Keeps track of the last opcode in the filtered bucket. unsigned LastOpcFiltered; - // Number of instructions which fall under VariableInstructions category. - unsigned NumVariable; - public: - unsigned getNumFiltered() { return NumFiltered; } - unsigned getNumVariable() { return NumVariable; } - unsigned getSingletonOpc() { + unsigned getNumFiltered() const { return NumFiltered; } + unsigned getSingletonOpc() const { assert(NumFiltered == 1); return LastOpcFiltered; } // Return the filter chooser for the group of instructions without constant // segment values. - FilterChooser &getVariableFC() { + const FilterChooser &getVariableFC() const { assert(NumFiltered == 1); assert(FilterChooserMap.size() == 1); return *(FilterChooserMap.find((unsigned)-1)->second); @@ -177,7 +174,7 @@ public: void recurse(); // Emit code to decode instructions given a segment or segments of bits. - void emit(raw_ostream &o, unsigned &Indentation); + void emit(raw_ostream &o, unsigned &Indentation) const; // Returns the number of fanout produced by the filter. More fanout implies // the filter distinguishes more categories of instructions. @@ -217,10 +214,10 @@ protected: const std::vector<const CodeGenInstruction*> &AllInstructions; // Vector of uid's for this filter chooser to work on. - const std::vector<unsigned> Opcodes; + const std::vector<unsigned> &Opcodes; // Lookup table for the operand decoding of instructions. - std::map<unsigned, std::vector<OperandInfo> > &Operands; + const std::map<unsigned, std::vector<OperandInfo> > &Operands; // Vector of candidate filters. std::vector<Filter> Filters; @@ -230,7 +227,7 @@ protected: std::vector<bit_value_t> FilterBitValues; // Links to the FilterChooser above us in the decoding tree. - FilterChooser *Parent; + const FilterChooser *Parent; // Index of the best filter from Filters. int BestIndex; @@ -242,19 +239,19 @@ protected: const FixedLenDecoderEmitter *Emitter; public: - FilterChooser(const FilterChooser &FC) : - AllInstructions(FC.AllInstructions), Opcodes(FC.Opcodes), + FilterChooser(const FilterChooser &FC) + : AllInstructions(FC.AllInstructions), Opcodes(FC.Opcodes), Operands(FC.Operands), Filters(FC.Filters), FilterBitValues(FC.FilterBitValues), Parent(FC.Parent), - BestIndex(FC.BestIndex), BitWidth(FC.BitWidth), - Emitter(FC.Emitter) { } + BestIndex(FC.BestIndex), BitWidth(FC.BitWidth), + Emitter(FC.Emitter) { } FilterChooser(const std::vector<const CodeGenInstruction*> &Insts, const std::vector<unsigned> &IDs, - std::map<unsigned, std::vector<OperandInfo> > &Ops, + const std::map<unsigned, std::vector<OperandInfo> > &Ops, unsigned BW, - const FixedLenDecoderEmitter *E) : - AllInstructions(Insts), Opcodes(IDs), Operands(Ops), Filters(), + const FixedLenDecoderEmitter *E) + : AllInstructions(Insts), Opcodes(IDs), Operands(Ops), Filters(), Parent(NULL), BestIndex(-1), BitWidth(BW), Emitter(E) { for (unsigned i = 0; i < BitWidth; ++i) FilterBitValues.push_back(BIT_UNFILTERED); @@ -264,10 +261,10 @@ public: FilterChooser(const std::vector<const CodeGenInstruction*> &Insts, const std::vector<unsigned> &IDs, - std::map<unsigned, std::vector<OperandInfo> > &Ops, - std::vector<bit_value_t> &ParentFilterBitValues, - FilterChooser &parent) : - AllInstructions(Insts), Opcodes(IDs), Operands(Ops), + const std::map<unsigned, std::vector<OperandInfo> > &Ops, + const std::vector<bit_value_t> &ParentFilterBitValues, + const FilterChooser &parent) + : AllInstructions(Insts), Opcodes(IDs), Operands(Ops), Filters(), FilterBitValues(ParentFilterBitValues), Parent(&parent), BestIndex(-1), BitWidth(parent.BitWidth), Emitter(parent.Emitter) { @@ -275,18 +272,31 @@ public: } // The top level filter chooser has NULL as its parent. - bool isTopLevel() { return Parent == NULL; } + bool isTopLevel() const { return Parent == NULL; } // Emit the top level typedef and decodeInstruction() function. - void emitTop(raw_ostream &o, unsigned Indentation, std::string Namespace); + void emitTop(raw_ostream &o, unsigned Indentation, + const std::string &Namespace) const; protected: // Populates the insn given the uid. void insnWithID(insn_t &Insn, unsigned Opcode) const { BitsInit &Bits = getBitsField(*AllInstructions[Opcode]->TheDef, "Inst"); - for (unsigned i = 0; i < BitWidth; ++i) - Insn.push_back(bitFromBits(Bits, i)); + // We may have a SoftFail bitmask, which specifies a mask where an encoding + // may differ from the value in "Inst" and yet still be valid, but the + // disassembler should return SoftFail instead of Success. + // + // This is used for marking UNPREDICTABLE instructions in the ARM world. + BitsInit *SFBits = + AllInstructions[Opcode]->TheDef->getValueAsBitsInit("SoftFail"); + + for (unsigned i = 0; i < BitWidth; ++i) { + if (SFBits && bitFromBits(*SFBits, i) == BIT_TRUE) + Insn.push_back(BIT_UNSET); + else + Insn.push_back(bitFromBits(Bits, i)); + } } // Returns the record name. @@ -300,15 +310,16 @@ protected: // Returns false if there exists any uninitialized bit value in the range. // Returns true, otherwise. bool fieldFromInsn(uint64_t &Field, insn_t &Insn, unsigned StartBit, - unsigned NumBits) const; + unsigned NumBits) const; /// dumpFilterArray - dumpFilterArray prints out debugging info for the given /// filter array as a series of chars. - void dumpFilterArray(raw_ostream &o, std::vector<bit_value_t> & filter); + void dumpFilterArray(raw_ostream &o, + const std::vector<bit_value_t> & filter) const; /// dumpStack - dumpStack traverses the filter chooser chain and calls /// dumpFilterArray on each filter chooser up to the top level one. - void dumpStack(raw_ostream &o, const char *prefix); + void dumpStack(raw_ostream &o, const char *prefix) const; Filter &bestFilter() { assert(BestIndex != -1 && "BestIndex not set"); @@ -316,9 +327,9 @@ protected: } // Called from Filter::recurse() when singleton exists. For debug purpose. - void SingletonExists(unsigned Opc); + void SingletonExists(unsigned Opc) const; - bool PositionFiltered(unsigned i) { + bool PositionFiltered(unsigned i) const { return ValueSet(FilterBitValues[i]); } @@ -327,31 +338,37 @@ protected: // Inst{20} = 1 && Inst{3-0} == 0b1111 represents two islands of yet-to-be // decoded bits in order to verify that the instruction matches the Opcode. unsigned getIslands(std::vector<unsigned> &StartBits, - std::vector<unsigned> &EndBits, std::vector<uint64_t> &FieldVals, - insn_t &Insn); + std::vector<unsigned> &EndBits, + std::vector<uint64_t> &FieldVals, + const insn_t &Insn) const; // Emits code to check the Predicates member of an instruction are true. // Returns true if predicate matches were emitted, false otherwise. - bool emitPredicateMatch(raw_ostream &o, unsigned &Indentation,unsigned Opc); + bool emitPredicateMatch(raw_ostream &o, unsigned &Indentation, + unsigned Opc) const; + + void emitSoftFailCheck(raw_ostream &o, unsigned Indentation, + unsigned Opc) const; // Emits code to decode the singleton. Return true if we have matched all the // well-known bits. - bool emitSingletonDecoder(raw_ostream &o, unsigned &Indentation,unsigned Opc); + bool emitSingletonDecoder(raw_ostream &o, unsigned &Indentation, + unsigned Opc) const; // Emits code to decode the singleton, and then to decode the rest. - void emitSingletonDecoder(raw_ostream &o, unsigned &Indentation,Filter &Best); + void emitSingletonDecoder(raw_ostream &o, unsigned &Indentation, + const Filter &Best) const; void emitBinaryParser(raw_ostream &o , unsigned &Indentation, - OperandInfo &OpInfo); + const OperandInfo &OpInfo) const; // Assign a single filter and run with it. - void runSingleFilter(FilterChooser &owner, unsigned startBit, unsigned numBit, - bool mixed); + void runSingleFilter(unsigned startBit, unsigned numBit, bool mixed); // reportRegion is a helper function for filterProcessor to mark a region as // eligible for use as a filter region. void reportRegion(bitAttr_t RA, unsigned StartBit, unsigned BitIndex, - bool AllowMixed); + bool AllowMixed); // FilterProcessor scans the well-known encoding bits of the instructions and // builds up a list of candidate filters. It chooses the best filter and @@ -366,31 +383,30 @@ protected: // Emits code to decode our share of instructions. Returns true if the // emitted code causes a return, which occurs if we know how to decode // the instruction at this level or the instruction is not decodeable. - bool emit(raw_ostream &o, unsigned &Indentation); + bool emit(raw_ostream &o, unsigned &Indentation) const; }; /////////////////////////// // // -// Filter Implmenetation // +// Filter Implementation // // // /////////////////////////// -Filter::Filter(const Filter &f) : - Owner(f.Owner), StartBit(f.StartBit), NumBits(f.NumBits), Mixed(f.Mixed), - FilteredInstructions(f.FilteredInstructions), - VariableInstructions(f.VariableInstructions), - FilterChooserMap(f.FilterChooserMap), NumFiltered(f.NumFiltered), - LastOpcFiltered(f.LastOpcFiltered), NumVariable(f.NumVariable) { +Filter::Filter(const Filter &f) + : Owner(f.Owner), StartBit(f.StartBit), NumBits(f.NumBits), Mixed(f.Mixed), + FilteredInstructions(f.FilteredInstructions), + VariableInstructions(f.VariableInstructions), + FilterChooserMap(f.FilterChooserMap), NumFiltered(f.NumFiltered), + LastOpcFiltered(f.LastOpcFiltered) { } Filter::Filter(FilterChooser &owner, unsigned startBit, unsigned numBits, - bool mixed) : Owner(&owner), StartBit(startBit), NumBits(numBits), - Mixed(mixed) { + bool mixed) + : Owner(&owner), StartBit(startBit), NumBits(numBits), Mixed(mixed) { assert(StartBit + NumBits - 1 < Owner->BitWidth); NumFiltered = 0; LastOpcFiltered = 0; - NumVariable = 0; for (unsigned i = 0, e = Owner->Opcodes.size(); i != e; ++i) { insn_t Insn; @@ -409,10 +425,9 @@ Filter::Filter(FilterChooser &owner, unsigned startBit, unsigned numBits, FilteredInstructions[Field].push_back(LastOpcFiltered); ++NumFiltered; } else { - // Some of the encoding bit(s) are unspecfied. This contributes to + // Some of the encoding bit(s) are unspecified. This contributes to // one additional member of "Variable" instructions. VariableInstructions.push_back(Owner->Opcodes[i]); - ++NumVariable; } } @@ -421,7 +436,7 @@ Filter::Filter(FilterChooser &owner, unsigned startBit, unsigned numBits, } Filter::~Filter() { - std::map<unsigned, FilterChooser*>::iterator filterIterator; + std::map<unsigned, const FilterChooser*>::iterator filterIterator; for (filterIterator = FilterChooserMap.begin(); filterIterator != FilterChooserMap.end(); filterIterator++) { @@ -450,7 +465,7 @@ void Filter::recurse() { // Delegates to an inferior filter chooser for further processing on this // group of instructions whose segment values are variable. - FilterChooserMap.insert(std::pair<unsigned, FilterChooser*>( + FilterChooserMap.insert(std::pair<unsigned, const FilterChooser*>( (unsigned)-1, new FilterChooser(Owner->AllInstructions, VariableInstructions, @@ -483,7 +498,7 @@ void Filter::recurse() { // Delegates to an inferior filter chooser for further processing on this // category of instructions. - FilterChooserMap.insert(std::pair<unsigned, FilterChooser*>( + FilterChooserMap.insert(std::pair<unsigned, const FilterChooser*>( mapIterator->first, new FilterChooser(Owner->AllInstructions, mapIterator->second, @@ -495,7 +510,7 @@ void Filter::recurse() { } // Emit code to decode instructions given a segment or segments of bits. -void Filter::emit(raw_ostream &o, unsigned &Indentation) { +void Filter::emit(raw_ostream &o, unsigned &Indentation) const { o.indent(Indentation) << "// Check Inst{"; if (NumBits > 1) @@ -507,7 +522,7 @@ void Filter::emit(raw_ostream &o, unsigned &Indentation) { << "(insn, " << StartBit << ", " << NumBits << ")) {\n"; - std::map<unsigned, FilterChooser*>::iterator filterIterator; + std::map<unsigned, const FilterChooser*>::const_iterator filterIterator; bool DefaultCase = false; for (filterIterator = FilterChooserMap.begin(); @@ -537,12 +552,12 @@ void Filter::emit(raw_ostream &o, unsigned &Indentation) { // encoding bits do not match exactly. if (!DefaultCase) { ++Indentation; ++Indentation; } - bool finished = filterIterator->second->emit(o, Indentation); + filterIterator->second->emit(o, Indentation); // For top level default case, there's no need for a break statement. if (Owner->isTopLevel() && DefaultCase) break; - if (!finished) - o.indent(Indentation) << "break;\n"; + + o.indent(Indentation) << "break;\n"; if (!DefaultCase) { --Indentation; --Indentation; } } @@ -571,13 +586,17 @@ unsigned Filter::usefulness() const { // Emit the top level typedef and decodeInstruction() function. void FilterChooser::emitTop(raw_ostream &o, unsigned Indentation, - std::string Namespace) { + const std::string &Namespace) const { o.indent(Indentation) << - "static MCDisassembler::DecodeStatus decode" << Namespace << "Instruction" << BitWidth - << "(MCInst &MI, uint" << BitWidth << "_t insn, uint64_t Address, " + "static MCDisassembler::DecodeStatus decode" << Namespace << "Instruction" + << BitWidth << "(MCInst &MI, uint" << BitWidth + << "_t insn, uint64_t Address, " << "const void *Decoder, const MCSubtargetInfo &STI) {\n"; - o.indent(Indentation) << " unsigned tmp = 0;\n (void)tmp;\n" << Emitter->Locals << "\n"; + o.indent(Indentation) << " unsigned tmp = 0;\n"; + o.indent(Indentation) << " (void)tmp;\n"; + o.indent(Indentation) << Emitter->Locals << "\n"; o.indent(Indentation) << " uint64_t Bits = STI.getFeatureBits();\n"; + o.indent(Indentation) << " (void)Bits;\n"; ++Indentation; ++Indentation; // Emits code to decode the instructions. @@ -598,7 +617,7 @@ void FilterChooser::emitTop(raw_ostream &o, unsigned Indentation, // Returns false if and on the first uninitialized bit value encountered. // Returns true, otherwise. bool FilterChooser::fieldFromInsn(uint64_t &Field, insn_t &Insn, - unsigned StartBit, unsigned NumBits) const { + unsigned StartBit, unsigned NumBits) const { Field = 0; for (unsigned i = 0; i < NumBits; ++i) { @@ -615,7 +634,7 @@ bool FilterChooser::fieldFromInsn(uint64_t &Field, insn_t &Insn, /// dumpFilterArray - dumpFilterArray prints out debugging info for the given /// filter array as a series of chars. void FilterChooser::dumpFilterArray(raw_ostream &o, - std::vector<bit_value_t> &filter) { + const std::vector<bit_value_t> &filter) const { unsigned bitIndex; for (bitIndex = BitWidth; bitIndex > 0; bitIndex--) { @@ -638,8 +657,8 @@ void FilterChooser::dumpFilterArray(raw_ostream &o, /// dumpStack - dumpStack traverses the filter chooser chain and calls /// dumpFilterArray on each filter chooser up to the top level one. -void FilterChooser::dumpStack(raw_ostream &o, const char *prefix) { - FilterChooser *current = this; +void FilterChooser::dumpStack(raw_ostream &o, const char *prefix) const { + const FilterChooser *current = this; while (current) { o << prefix; @@ -650,7 +669,7 @@ void FilterChooser::dumpStack(raw_ostream &o, const char *prefix) { } // Called from Filter::recurse() when singleton exists. For debug purpose. -void FilterChooser::SingletonExists(unsigned Opc) { +void FilterChooser::SingletonExists(unsigned Opc) const { insn_t Insn0; insnWithID(Insn0, Opc); @@ -663,7 +682,7 @@ void FilterChooser::SingletonExists(unsigned Opc) { errs() << '\n'; dumpStack(errs(), "\t\t"); - for (unsigned i = 0; i < Opcodes.size(); i++) { + for (unsigned i = 0; i < Opcodes.size(); ++i) { const std::string &Name = nameWithID(Opcodes[i]); errs() << '\t' << Name << " "; @@ -678,8 +697,9 @@ void FilterChooser::SingletonExists(unsigned Opc) { // Inst{20} = 1 && Inst{3-0} == 0b1111 represents two islands of yet-to-be // decoded bits in order to verify that the instruction matches the Opcode. unsigned FilterChooser::getIslands(std::vector<unsigned> &StartBits, - std::vector<unsigned> &EndBits, std::vector<uint64_t> &FieldVals, - insn_t &Insn) { + std::vector<unsigned> &EndBits, + std::vector<uint64_t> &FieldVals, + const insn_t &Insn) const { unsigned Num, BitNo; Num = BitNo = 0; @@ -695,9 +715,7 @@ unsigned FilterChooser::getIslands(std::vector<unsigned> &StartBits, Val = Value(Insn[i]); bool Filtered = PositionFiltered(i); switch (State) { - default: - assert(0 && "Unreachable code!"); - break; + default: llvm_unreachable("Unreachable code!"); case 0: case 1: if (Filtered || Val == -1) @@ -736,17 +754,17 @@ unsigned FilterChooser::getIslands(std::vector<unsigned> &StartBits, } void FilterChooser::emitBinaryParser(raw_ostream &o, unsigned &Indentation, - OperandInfo &OpInfo) { - std::string &Decoder = OpInfo.Decoder; + const OperandInfo &OpInfo) const { + const std::string &Decoder = OpInfo.Decoder; if (OpInfo.numFields() == 1) { - OperandInfo::iterator OI = OpInfo.begin(); + OperandInfo::const_iterator OI = OpInfo.begin(); o.indent(Indentation) << " tmp = fieldFromInstruction" << BitWidth << "(insn, " << OI->Base << ", " << OI->Width << ");\n"; } else { o.indent(Indentation) << " tmp = 0;\n"; - for (OperandInfo::iterator OI = OpInfo.begin(), OE = OpInfo.end(); + for (OperandInfo::const_iterator OI = OpInfo.begin(), OE = OpInfo.end(); OI != OE; ++OI) { o.indent(Indentation) << " tmp |= (fieldFromInstruction" << BitWidth << "(insn, " << OI->Base << ", " << OI->Width @@ -756,14 +774,15 @@ void FilterChooser::emitBinaryParser(raw_ostream &o, unsigned &Indentation, if (Decoder != "") o.indent(Indentation) << " " << Emitter->GuardPrefix << Decoder - << "(MI, tmp, Address, Decoder)" << Emitter->GuardPostfix << "\n"; + << "(MI, tmp, Address, Decoder)" + << Emitter->GuardPostfix << "\n"; else o.indent(Indentation) << " MI.addOperand(MCOperand::CreateImm(tmp));\n"; } static void emitSinglePredicateMatch(raw_ostream &o, StringRef str, - std::string PredicateNamespace) { + const std::string &PredicateNamespace) { if (str[0] == '!') o << "!(Bits & " << PredicateNamespace << "::" << str.slice(1,str.size()) << ")"; @@ -772,8 +791,9 @@ static void emitSinglePredicateMatch(raw_ostream &o, StringRef str, } bool FilterChooser::emitPredicateMatch(raw_ostream &o, unsigned &Indentation, - unsigned Opc) { - ListInit *Predicates = AllInstructions[Opc]->TheDef->getValueAsListInit("Predicates"); + unsigned Opc) const { + ListInit *Predicates = + AllInstructions[Opc]->TheDef->getValueAsListInit("Predicates"); for (unsigned i = 0; i < Predicates->getSize(); ++i) { Record *Pred = Predicates->getElementAsRecord(i); if (!Pred->getValue("AssemblerMatcherPredicate")) @@ -799,10 +819,70 @@ bool FilterChooser::emitPredicateMatch(raw_ostream &o, unsigned &Indentation, return Predicates->getSize() > 0; } +void FilterChooser::emitSoftFailCheck(raw_ostream &o, unsigned Indentation, + unsigned Opc) const { + BitsInit *SFBits = + AllInstructions[Opc]->TheDef->getValueAsBitsInit("SoftFail"); + if (!SFBits) return; + BitsInit *InstBits = AllInstructions[Opc]->TheDef->getValueAsBitsInit("Inst"); + + APInt PositiveMask(BitWidth, 0ULL); + APInt NegativeMask(BitWidth, 0ULL); + for (unsigned i = 0; i < BitWidth; ++i) { + bit_value_t B = bitFromBits(*SFBits, i); + bit_value_t IB = bitFromBits(*InstBits, i); + + if (B != BIT_TRUE) continue; + + switch (IB) { + case BIT_FALSE: + // The bit is meant to be false, so emit a check to see if it is true. + PositiveMask.setBit(i); + break; + case BIT_TRUE: + // The bit is meant to be true, so emit a check to see if it is false. + NegativeMask.setBit(i); + break; + default: + // The bit is not set; this must be an error! + StringRef Name = AllInstructions[Opc]->TheDef->getName(); + errs() << "SoftFail Conflict: bit SoftFail{" << i << "} in " + << Name + << " is set but Inst{" << i <<"} is unset!\n" + << " - You can only mark a bit as SoftFail if it is fully defined" + << " (1/0 - not '?') in Inst\n"; + o << "#error SoftFail Conflict, " << Name << "::SoftFail{" << i + << "} set but Inst{" << i << "} undefined!\n"; + } + } + + bool NeedPositiveMask = PositiveMask.getBoolValue(); + bool NeedNegativeMask = NegativeMask.getBoolValue(); + + if (!NeedPositiveMask && !NeedNegativeMask) + return; + + std::string PositiveMaskStr = PositiveMask.toString(16, /*signed=*/false); + std::string NegativeMaskStr = NegativeMask.toString(16, /*signed=*/false); + StringRef BitExt = ""; + if (BitWidth > 32) + BitExt = "ULL"; + + o.indent(Indentation) << "if ("; + if (NeedPositiveMask) + o << "insn & 0x" << PositiveMaskStr << BitExt; + if (NeedPositiveMask && NeedNegativeMask) + o << " || "; + if (NeedNegativeMask) + o << "~insn & 0x" << NegativeMaskStr << BitExt; + o << ")\n"; + o.indent(Indentation+2) << "S = MCDisassembler::SoftFail;\n"; +} + // Emits code to decode the singleton. Return true if we have matched all the // well-known bits. bool FilterChooser::emitSingletonDecoder(raw_ostream &o, unsigned &Indentation, - unsigned Opc) { + unsigned Opc) const { std::vector<unsigned> StartBits; std::vector<unsigned> EndBits; std::vector<uint64_t> FieldVals; @@ -821,22 +901,26 @@ bool FilterChooser::emitSingletonDecoder(raw_ostream &o, unsigned &Indentation, if (!emitPredicateMatch(o, Indentation, Opc)) o << "1"; o << ") {\n"; + emitSoftFailCheck(o, Indentation+2, Opc); o.indent(Indentation) << " MI.setOpcode(" << Opc << ");\n"; - std::vector<OperandInfo>& InsnOperands = Operands[Opc]; - for (std::vector<OperandInfo>::iterator + std::map<unsigned, std::vector<OperandInfo> >::const_iterator OpIter = + Operands.find(Opc); + const std::vector<OperandInfo>& InsnOperands = OpIter->second; + for (std::vector<OperandInfo>::const_iterator I = InsnOperands.begin(), E = InsnOperands.end(); I != E; ++I) { // If a custom instruction decoder was specified, use that. if (I->numFields() == 0 && I->Decoder.size()) { o.indent(Indentation) << " " << Emitter->GuardPrefix << I->Decoder - << "(MI, insn, Address, Decoder)" << Emitter->GuardPostfix << "\n"; + << "(MI, insn, Address, Decoder)" + << Emitter->GuardPostfix << "\n"; break; } emitBinaryParser(o, Indentation, *I); } - o.indent(Indentation) << " return " << Emitter->ReturnOK << "; // " << nameWithID(Opc) - << '\n'; + o.indent(Indentation) << " return " << Emitter->ReturnOK << "; // " + << nameWithID(Opc) << '\n'; o.indent(Indentation) << "}\n"; // Closing predicate block. return true; } @@ -870,21 +954,25 @@ bool FilterChooser::emitSingletonDecoder(raw_ostream &o, unsigned &Indentation, else o << ") {\n"; } + emitSoftFailCheck(o, Indentation+2, Opc); o.indent(Indentation) << " MI.setOpcode(" << Opc << ");\n"; - std::vector<OperandInfo>& InsnOperands = Operands[Opc]; - for (std::vector<OperandInfo>::iterator + std::map<unsigned, std::vector<OperandInfo> >::const_iterator OpIter = + Operands.find(Opc); + const std::vector<OperandInfo>& InsnOperands = OpIter->second; + for (std::vector<OperandInfo>::const_iterator I = InsnOperands.begin(), E = InsnOperands.end(); I != E; ++I) { // If a custom instruction decoder was specified, use that. if (I->numFields() == 0 && I->Decoder.size()) { o.indent(Indentation) << " " << Emitter->GuardPrefix << I->Decoder - << "(MI, insn, Address, Decoder)" << Emitter->GuardPostfix << "\n"; + << "(MI, insn, Address, Decoder)" + << Emitter->GuardPostfix << "\n"; break; } emitBinaryParser(o, Indentation, *I); } - o.indent(Indentation) << " return " << Emitter->ReturnOK << "; // " << nameWithID(Opc) - << '\n'; + o.indent(Indentation) << " return " << Emitter->ReturnOK << "; // " + << nameWithID(Opc) << '\n'; o.indent(Indentation) << "}\n"; return false; @@ -892,7 +980,7 @@ bool FilterChooser::emitSingletonDecoder(raw_ostream &o, unsigned &Indentation, // Emits code to decode the singleton, and then to decode the rest. void FilterChooser::emitSingletonDecoder(raw_ostream &o, unsigned &Indentation, - Filter &Best) { + const Filter &Best) const { unsigned Opc = Best.getSingletonOpc(); @@ -908,8 +996,8 @@ void FilterChooser::emitSingletonDecoder(raw_ostream &o, unsigned &Indentation, // Assign a single filter and run with it. Top level API client can initialize // with a single filter to start the filtering process. -void FilterChooser::runSingleFilter(FilterChooser &owner, unsigned startBit, - unsigned numBit, bool mixed) { +void FilterChooser::runSingleFilter(unsigned startBit, unsigned numBit, + bool mixed) { Filters.clear(); Filter F(*this, startBit, numBit, true); Filters.push_back(F); @@ -920,7 +1008,7 @@ void FilterChooser::runSingleFilter(FilterChooser &owner, unsigned startBit, // reportRegion is a helper function for filterProcessor to mark a region as // eligible for use as a filter region. void FilterChooser::reportRegion(bitAttr_t RA, unsigned StartBit, - unsigned BitIndex, bool AllowMixed) { + unsigned BitIndex, bool AllowMixed) { if (RA == ATTR_MIXED && AllowMixed) Filters.push_back(Filter(*this, StartBit, BitIndex - StartBit, true)); else if (RA == ATTR_ALL_SET && !AllowMixed) @@ -957,8 +1045,7 @@ bool FilterChooser::filterProcessor(bool AllowMixed, bool Greedy) { // Look for islands of undecoded bits of any instruction. if (getIslands(StartBits, EndBits, FieldVals, Insn) > 0) { // Found an instruction with island(s). Now just assign a filter. - runSingleFilter(*this, StartBits[0], EndBits[0] - StartBits[0] + 1, - true); + runSingleFilter(StartBits[0], EndBits[0] - StartBits[0] + 1, true); return true; } } @@ -1066,7 +1153,7 @@ bool FilterChooser::filterProcessor(bool AllowMixed, bool Greedy) { RA = ATTR_MIXED; break; default: - assert(0 && "Unexpected bitAttr!"); + llvm_unreachable("Unexpected bitAttr!"); } break; case ATTR_ALL_SET: @@ -1087,7 +1174,7 @@ bool FilterChooser::filterProcessor(bool AllowMixed, bool Greedy) { RA = ATTR_MIXED; break; default: - assert(0 && "Unexpected bitAttr!"); + llvm_unreachable("Unexpected bitAttr!"); } break; case ATTR_MIXED: @@ -1109,13 +1196,13 @@ bool FilterChooser::filterProcessor(bool AllowMixed, bool Greedy) { case ATTR_MIXED: break; default: - assert(0 && "Unexpected bitAttr!"); + llvm_unreachable("Unexpected bitAttr!"); } break; case ATTR_ALL_UNSET: - assert(0 && "regionAttr state machine has no ATTR_UNSET state"); + llvm_unreachable("regionAttr state machine has no ATTR_UNSET state"); case ATTR_FILTERED: - assert(0 && "regionAttr state machine has no ATTR_FILTERED state"); + llvm_unreachable("regionAttr state machine has no ATTR_FILTERED state"); } } @@ -1189,7 +1276,7 @@ void FilterChooser::doFilter() { // Emits code to decode our share of instructions. Returns true if the // emitted code causes a return, which occurs if we know how to decode // the instruction at this level or the instruction is not decodeable. -bool FilterChooser::emit(raw_ostream &o, unsigned &Indentation) { +bool FilterChooser::emit(raw_ostream &o, unsigned &Indentation) const { if (Opcodes.size() == 1) // There is only one instruction in the set, which is great! // Call emitSingletonDecoder() to see whether there are any remaining @@ -1198,11 +1285,11 @@ bool FilterChooser::emit(raw_ostream &o, unsigned &Indentation) { // Choose the best filter to do the decodings! if (BestIndex != -1) { - Filter &Best = bestFilter(); + const Filter &Best = Filters[BestIndex]; if (Best.getNumFiltered() == 1) emitSingletonDecoder(o, Indentation, Best); else - bestFilter().emit(o, Indentation); + Best.emit(o, Indentation); return false; } @@ -1222,7 +1309,7 @@ bool FilterChooser::emit(raw_ostream &o, unsigned &Indentation) { dumpStack(errs(), "\t\t"); - for (unsigned i = 0; i < Opcodes.size(); i++) { + for (unsigned i = 0; i < Opcodes.size(); ++i) { const std::string &Name = nameWithID(Opcodes[i]); errs() << '\t' << Name << " "; @@ -1234,9 +1321,8 @@ bool FilterChooser::emit(raw_ostream &o, unsigned &Indentation) { return true; } -static bool populateInstruction(const CodeGenInstruction &CGI, - unsigned Opc, - std::map<unsigned, std::vector<OperandInfo> >& Operands){ +static bool populateInstruction(const CodeGenInstruction &CGI, unsigned Opc, + std::map<unsigned, std::vector<OperandInfo> > &Operands){ const Record &Def = *CGI.TheDef; // If all the bit positions are not specified; do not decode this instruction. // We are bound to fail! For proper disassembly, the well-known encoding bits @@ -1290,7 +1376,7 @@ static bool populateInstruction(const CodeGenInstruction &CGI, } // For each operand, see if we can figure out where it is encoded. - for (std::vector<std::pair<Init*, std::string> >::iterator + for (std::vector<std::pair<Init*, std::string> >::const_iterator NI = InOutOperands.begin(), NE = InOutOperands.end(); NI != NE; ++NI) { std::string Decoder = ""; @@ -1435,8 +1521,7 @@ static void emitHelper(llvm::raw_ostream &o, unsigned BitWidth) { } // Emits disassembler code for instruction decoding. -void FixedLenDecoderEmitter::run(raw_ostream &o) -{ +void FixedLenDecoderEmitter::run(raw_ostream &o) { o << "#include \"llvm/MC/MCInst.h\"\n"; o << "#include \"llvm/Support/DataTypes.h\"\n"; o << "#include <assert.h>\n"; @@ -1444,14 +1529,15 @@ void FixedLenDecoderEmitter::run(raw_ostream &o) o << "namespace llvm {\n\n"; // Parameterize the decoders based on namespace and instruction width. - NumberedInstructions = Target.getInstructionsByEnumValue(); + const std::vector<const CodeGenInstruction*> &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[i]; - Record *Def = Inst->TheDef; + const Record *Def = Inst->TheDef; unsigned Size = Def->getValueAsInt("Size"); if (Def->getValueAsString("Namespace") == "TargetOpcode" || Def->getValueAsBit("isPseudo") || @@ -1470,7 +1556,7 @@ void FixedLenDecoderEmitter::run(raw_ostream &o) std::set<unsigned> Sizes; for (std::map<std::pair<std::string, unsigned>, - std::vector<unsigned> >::iterator + std::vector<unsigned> >::const_iterator I = OpcMap.begin(), E = OpcMap.end(); I != E; ++I) { // If we haven't visited this instruction width before, emit the // helper method to extract fields. diff --git a/utils/TableGen/FixedLenDecoderEmitter.h b/utils/TableGen/FixedLenDecoderEmitter.h index 2df5448..195297c 100644 --- a/utils/TableGen/FixedLenDecoderEmitter.h +++ b/utils/TableGen/FixedLenDecoderEmitter.h @@ -39,12 +39,12 @@ struct OperandInfo { Fields.push_back(EncodingField(Base, Width, Offset)); } - unsigned numFields() { return Fields.size(); } + unsigned numFields() const { return Fields.size(); } - typedef std::vector<EncodingField>::iterator iterator; + typedef std::vector<EncodingField>::const_iterator const_iterator; - iterator begin() { return Fields.begin(); } - iterator end() { return Fields.end(); } + const_iterator begin() const { return Fields.begin(); } + const_iterator end() const { return Fields.end(); } }; class FixedLenDecoderEmitter : public TableGenBackend { @@ -52,12 +52,12 @@ public: FixedLenDecoderEmitter(RecordKeeper &R, std::string PredicateNamespace, std::string GPrefix = "if (", - std::string GPostfix = " == MCDisassembler::Fail) return MCDisassembler::Fail;", + std::string GPostfix = " == MCDisassembler::Fail)" + " return MCDisassembler::Fail;", std::string ROK = "MCDisassembler::Success", std::string RFail = "MCDisassembler::Fail", std::string L = "") : - Records(R), Target(R), - NumberedInstructions(Target.getInstructionsByEnumValue()), + Target(R), PredicateNamespace(PredicateNamespace), GuardPrefix(GPrefix), GuardPostfix(GPostfix), ReturnOK(ROK), ReturnFail(RFail), Locals(L) {} @@ -66,11 +66,7 @@ public: void run(raw_ostream &o); private: - RecordKeeper &Records; CodeGenTarget Target; - std::vector<const CodeGenInstruction*> NumberedInstructions; - std::vector<unsigned> Opcodes; - std::map<unsigned, std::vector<OperandInfo> > Operands; public: std::string PredicateNamespace; std::string GuardPrefix, GuardPostfix; diff --git a/utils/TableGen/InstrEnumEmitter.cpp b/utils/TableGen/InstrEnumEmitter.cpp deleted file mode 100644 index 5981afd..0000000 --- a/utils/TableGen/InstrEnumEmitter.cpp +++ /dev/null @@ -1,48 +0,0 @@ -//===- InstrEnumEmitter.cpp - Generate Instruction Set Enums --------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This tablegen backend is responsible for emitting enums for each machine -// instruction. -// -//===----------------------------------------------------------------------===// - -#include "InstrEnumEmitter.h" -#include "CodeGenTarget.h" -#include "llvm/TableGen/Record.h" -#include <cstdio> -using namespace llvm; - -// runEnums - Print out enum values for all of the instructions. -void InstrEnumEmitter::run(raw_ostream &OS) { - EmitSourceFileHeader("Target Instruction Enum Values", OS); - OS << "namespace llvm {\n\n"; - - CodeGenTarget Target(Records); - - // We must emit the PHI opcode first... - std::string Namespace = Target.getInstNamespace(); - - if (Namespace.empty()) { - fprintf(stderr, "No instructions defined!\n"); - exit(1); - } - - const std::vector<const CodeGenInstruction*> &NumberedInstructions = - Target.getInstructionsByEnumValue(); - - OS << "namespace " << Namespace << " {\n"; - OS << " enum {\n"; - for (unsigned i = 0, e = NumberedInstructions.size(); i != e; ++i) { - OS << " " << NumberedInstructions[i]->TheDef->getName() - << "\t= " << i << ",\n"; - } - OS << " INSTRUCTION_LIST_END = " << NumberedInstructions.size() << "\n"; - OS << " };\n}\n"; - OS << "} // End llvm namespace \n"; -} diff --git a/utils/TableGen/InstrEnumEmitter.h b/utils/TableGen/InstrEnumEmitter.h deleted file mode 100644 index c29a309..0000000 --- a/utils/TableGen/InstrEnumEmitter.h +++ /dev/null @@ -1,33 +0,0 @@ -//===- InstrEnumEmitter.h - Generate Instruction Set Enums ------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This tablegen backend is responsible for emitting enums for each machine -// instruction. -// -//===----------------------------------------------------------------------===// - -#ifndef INSTRENUM_EMITTER_H -#define INSTRENUM_EMITTER_H - -#include "llvm/TableGen/TableGenBackend.h" - -namespace llvm { - -class InstrEnumEmitter : public TableGenBackend { - RecordKeeper &Records; -public: - InstrEnumEmitter(RecordKeeper &R) : Records(R) {} - - // run - Output the instruction set description, returning true on failure. - void run(raw_ostream &OS); -}; - -} // End llvm namespace - -#endif diff --git a/utils/TableGen/InstrInfoEmitter.cpp b/utils/TableGen/InstrInfoEmitter.cpp index 8341724..8b3efd3 100644 --- a/utils/TableGen/InstrInfoEmitter.cpp +++ b/utils/TableGen/InstrInfoEmitter.cpp @@ -14,14 +14,16 @@ #include "InstrInfoEmitter.h" #include "CodeGenTarget.h" +#include "SequenceToOffsetTable.h" #include "llvm/TableGen/Record.h" #include "llvm/ADT/StringExtras.h" #include <algorithm> +#include <cstdio> using namespace llvm; static void PrintDefList(const std::vector<Record*> &Uses, unsigned Num, raw_ostream &OS) { - OS << "static const unsigned ImplicitList" << Num << "[] = { "; + OS << "static const uint16_t ImplicitList" << Num << "[] = { "; for (unsigned i = 0, e = Uses.size(); i != e; ++i) OS << getQualifiedName(Uses[i]) << ", "; OS << "0 };\n"; @@ -106,6 +108,11 @@ InstrInfoEmitter::GetOperandInfo(const CodeGenInstruction &Inst) { if (Inst.Operands[i].Rec->isSubClassOf("OptionalDefOperand")) Res += "|(1<<MCOI::OptionalDef)"; + // Fill in operand type. + Res += ", MCOI::"; + assert(!Inst.Operands[i].OperandType.empty() && "Invalid operand type."); + Res += Inst.Operands[i].OperandType; + // Fill in constraint info. Res += ", "; @@ -121,11 +128,6 @@ InstrInfoEmitter::GetOperandInfo(const CodeGenInstruction &Inst) { " << 16) | (1 << MCOI::TIED_TO))"; } - // Fill in operand type. - Res += ", MCOI::"; - assert(!Inst.Operands[i].OperandType.empty() && "Invalid operand type."); - Res += Inst.Operands[i].OperandType; - Result.push_back(Res); } } @@ -203,7 +205,7 @@ void InstrInfoEmitter::run(raw_ostream &OS) { // Emit all of the MCInstrDesc records in their ENUM ordering. // - OS << "\nMCInstrDesc " << TargetName << "Insts[] = {\n"; + OS << "\nextern const MCInstrDesc " << TargetName << "Insts[] = {\n"; const std::vector<const CodeGenInstruction*> &NumberedInstructions = Target.getInstructionsByEnumValue(); @@ -212,10 +214,33 @@ void InstrInfoEmitter::run(raw_ostream &OS) { OperandInfoIDs, OS); OS << "};\n\n"; + // Build an array of instruction names + SequenceToOffsetTable<std::string> InstrNames; + for (unsigned i = 0, e = NumberedInstructions.size(); i != e; ++i) { + const CodeGenInstruction *Instr = NumberedInstructions[i]; + InstrNames.add(Instr->TheDef->getName()); + } + + InstrNames.layout(); + OS << "extern const char " << TargetName << "InstrNameData[] = {\n"; + InstrNames.emit(OS, printChar); + OS << "};\n\n"; + + OS << "extern const unsigned " << TargetName <<"InstrNameIndices[] = {"; + for (unsigned i = 0, e = NumberedInstructions.size(); i != e; ++i) { + if (i % 8 == 0) + OS << "\n "; + const CodeGenInstruction *Instr = NumberedInstructions[i]; + OS << InstrNames.get(Instr->TheDef->getName()) << "U, "; + } + + OS << "\n};\n\n"; + // MCInstrInfo initialization routine. OS << "static inline void Init" << TargetName << "MCInstrInfo(MCInstrInfo *II) {\n"; OS << " II->InitMCInstrInfo(" << TargetName << "Insts, " + << TargetName << "InstrNameIndices, " << TargetName << "InstrNameData, " << NumberedInstructions.size() << ");\n}\n\n"; OS << "} // End llvm namespace \n"; @@ -239,10 +264,13 @@ void InstrInfoEmitter::run(raw_ostream &OS) { OS << "#undef GET_INSTRINFO_CTOR\n"; OS << "namespace llvm {\n"; - OS << "extern MCInstrDesc " << TargetName << "Insts[];\n"; + OS << "extern const MCInstrDesc " << TargetName << "Insts[];\n"; + OS << "extern const unsigned " << TargetName << "InstrNameIndices[];\n"; + OS << "extern const char " << TargetName << "InstrNameData[];\n"; OS << ClassName << "::" << ClassName << "(int SO, int DO)\n" << " : TargetInstrInfoImpl(SO, DO) {\n" << " InitMCInstrInfo(" << TargetName << "Insts, " + << TargetName << "InstrNameIndices, " << TargetName << "InstrNameData, " << NumberedInstructions.size() << ");\n}\n"; OS << "} // End llvm namespace \n"; @@ -264,8 +292,7 @@ void InstrInfoEmitter::emitRecord(const CodeGenInstruction &Inst, unsigned Num, OS << Num << ",\t" << MinOperands << ",\t" << Inst.Operands.NumDefs << ",\t" << getItinClassNumber(Inst.TheDef) << ",\t" - << Inst.TheDef->getValueAsInt("Size") << ",\t\"" - << Inst.TheDef->getName() << "\", 0"; + << Inst.TheDef->getValueAsInt("Size") << ",\t0"; // Emit all of the target indepedent flags... if (Inst.isPseudo) OS << "|(1<<MCID::Pseudo)"; @@ -346,7 +373,7 @@ void InstrInfoEmitter::emitEnums(raw_ostream &OS) { // We must emit the PHI opcode first... std::string Namespace = Target.getInstNamespace(); - + if (Namespace.empty()) { fprintf(stderr, "No instructions defined!\n"); exit(1); diff --git a/utils/TableGen/InstrInfoEmitter.h b/utils/TableGen/InstrInfoEmitter.h index 1461e2c..f8d3ea5 100644 --- a/utils/TableGen/InstrInfoEmitter.h +++ b/utils/TableGen/InstrInfoEmitter.h @@ -31,19 +31,19 @@ class InstrInfoEmitter : public TableGenBackend { RecordKeeper &Records; CodeGenDAGPatterns CDP; std::map<std::string, unsigned> ItinClassMap; - + public: InstrInfoEmitter(RecordKeeper &R) : Records(R), CDP(R) { } - // run - Output the instruction set description, returning true on failure. + // run - Output the instruction set description. void run(raw_ostream &OS); private: void emitEnums(raw_ostream &OS); - typedef std::map<std::vector<std::string>, unsigned> OperandInfoMapTy; + typedef std::map<std::vector<std::string>, unsigned> OperandInfoMapTy; void emitRecord(const CodeGenInstruction &Inst, unsigned Num, - Record *InstrInfo, + Record *InstrInfo, std::map<std::vector<Record*>, unsigned> &EL, const OperandInfoMapTy &OpInfo, raw_ostream &OS); @@ -51,7 +51,7 @@ private: // Itinerary information. void GatherItinClasses(); unsigned getItinClassNumber(const Record *InstRec); - + // Operand information. void EmitOperandInfo(raw_ostream &OS, OperandInfoMapTy &OperandInfoIDs); std::vector<std::string> GetOperandInfo(const CodeGenInstruction &Inst); diff --git a/utils/TableGen/IntrinsicEmitter.cpp b/utils/TableGen/IntrinsicEmitter.cpp index 782b89e..8e1bae8 100644 --- a/utils/TableGen/IntrinsicEmitter.cpp +++ b/utils/TableGen/IntrinsicEmitter.cpp @@ -57,9 +57,6 @@ void IntrinsicEmitter::run(raw_ostream &OS) { // Emit intrinsic alias analysis mod/ref behavior. EmitModRefBehavior(Ints, OS); - // Emit a list of intrinsics with corresponding GCC builtins. - EmitGCCBuiltinList(Ints, OS); - // Emit code to translate GCC builtins into LLVM intrinsics. EmitIntrinsicToGCCBuiltinMap(Ints, OS); @@ -160,17 +157,20 @@ EmitIntrinsicToNameTable(const std::vector<CodeGenIntrinsic> &Ints, void IntrinsicEmitter:: EmitIntrinsicToOverloadTable(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS) { - OS << "// Intrinsic ID to overload table\n"; + OS << "// Intrinsic ID to overload bitset\n"; OS << "#ifdef GET_INTRINSIC_OVERLOAD_TABLE\n"; - OS << " // Note that entry #0 is the invalid intrinsic!\n"; + OS << "static const uint8_t OTable[] = {\n"; + OS << " 0"; for (unsigned i = 0, e = Ints.size(); i != e; ++i) { - OS << " "; + // Add one to the index so we emit a null bit for the invalid #0 intrinsic. + if ((i+1)%8 == 0) + OS << ",\n 0"; if (Ints[i].isOverloaded) - OS << "true"; - else - OS << "false"; - OS << ",\n"; + OS << " | (1<<" << (i+1)%8 << ')'; } + OS << "\n};\n\n"; + // OTable contains a true bit at the position if the intrinsic is overloaded. + OS << "return (OTable[id/8] & (1 << (id%8))) != 0;\n"; OS << "#endif\n\n"; } @@ -181,6 +181,8 @@ static void EmitTypeForValueType(raw_ostream &OS, MVT::SimpleValueType VT) { } else if (VT == MVT::Other) { // MVT::OtherVT is used to mean the empty struct type here. OS << "StructType::get(Context)"; + } else if (VT == MVT::f16) { + OS << "Type::getHalfTy(Context)"; } else if (VT == MVT::f32) { OS << "Type::getFloatTy(Context)"; } else if (VT == MVT::f64) { @@ -318,7 +320,7 @@ void IntrinsicEmitter::EmitVerifier(const std::vector<CodeGenIntrinsic> &Ints, OS << "// Verifier::visitIntrinsicFunctionCall code.\n"; OS << "#ifdef GET_INTRINSIC_VERIFIER\n"; OS << " switch (ID) {\n"; - OS << " default: assert(0 && \"Invalid intrinsic!\");\n"; + OS << " default: llvm_unreachable(\"Invalid intrinsic!\");\n"; // This checking can emit a lot of very common code. To reduce the amount of // code that we emit, batch up cases that have identical types. This avoids @@ -414,7 +416,7 @@ void IntrinsicEmitter::EmitGenerator(const std::vector<CodeGenIntrinsic> &Ints, OS << "// Code for generating Intrinsic function declarations.\n"; OS << "#ifdef GET_INTRINSIC_GENERATOR\n"; OS << " switch (id) {\n"; - OS << " default: assert(0 && \"Invalid intrinsic!\");\n"; + OS << " default: llvm_unreachable(\"Invalid intrinsic!\");\n"; // Similar to GET_INTRINSIC_VERIFIER, batch up cases that have identical // types. @@ -483,8 +485,7 @@ namespace { case CodeGenIntrinsic::ReadWriteMem: return MRK_none; } - assert(0 && "bad mod-ref kind"); - return MRK_none; + llvm_unreachable("bad mod-ref kind"); } struct AttributeComparator { @@ -516,37 +517,50 @@ EmitAttributes(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS) { else OS << "AttrListPtr Intrinsic::getAttributes(ID id) {\n"; - // Compute the maximum number of attribute arguments. - std::vector<const CodeGenIntrinsic*> sortedIntrinsics(Ints.size()); + // Compute the maximum number of attribute arguments and the map + typedef std::map<const CodeGenIntrinsic*, unsigned, + AttributeComparator> UniqAttrMapTy; + UniqAttrMapTy UniqAttributes; unsigned maxArgAttrs = 0; + unsigned AttrNum = 0; for (unsigned i = 0, e = Ints.size(); i != e; ++i) { const CodeGenIntrinsic &intrinsic = Ints[i]; - sortedIntrinsics[i] = &intrinsic; maxArgAttrs = std::max(maxArgAttrs, unsigned(intrinsic.ArgumentAttributes.size())); + unsigned &N = UniqAttributes[&intrinsic]; + if (N) continue; + assert(AttrNum < 256 && "Too many unique attributes for table!"); + N = ++AttrNum; } // Emit an array of AttributeWithIndex. Most intrinsics will have // at least one entry, for the function itself (index ~1), which is // usually nounwind. - OS << " AttributeWithIndex AWI[" << maxArgAttrs+1 << "];\n"; - OS << " unsigned NumAttrs = 0;\n"; - OS << " switch (id) {\n"; - OS << " default: break;\n"; + OS << " static const uint8_t IntrinsicsToAttributesMap[] = {\n"; - AttributeComparator precedes; + for (unsigned i = 0, e = Ints.size(); i != e; ++i) { + const CodeGenIntrinsic &intrinsic = Ints[i]; - std::stable_sort(sortedIntrinsics.begin(), sortedIntrinsics.end(), precedes); + OS << " " << UniqAttributes[&intrinsic] << ", // " + << intrinsic.Name << "\n"; + } + OS << " };\n\n"; - for (unsigned i = 0, e = sortedIntrinsics.size(); i != e; ++i) { - const CodeGenIntrinsic &intrinsic = *sortedIntrinsics[i]; - OS << " case " << TargetPrefix << "Intrinsic::" - << intrinsic.EnumName << ":\n"; + OS << " AttributeWithIndex AWI[" << maxArgAttrs+1 << "];\n"; + OS << " unsigned NumAttrs = 0;\n"; + OS << " if (id != 0) {\n"; + OS << " switch(IntrinsicsToAttributesMap[id - "; + if (TargetOnly) + OS << "Intrinsic::num_intrinsics"; + else + OS << "1"; + OS << "]) {\n"; + OS << " default: llvm_unreachable(\"Invalid attribute number\");\n"; + for (UniqAttrMapTy::const_iterator I = UniqAttributes.begin(), + E = UniqAttributes.end(); I != E; ++I) { + OS << " case " << I->second << ":\n"; - // Fill out the case if this is the last case for this range of - // intrinsics. - if (i + 1 != e && !precedes(&intrinsic, sortedIntrinsics[i + 1])) - continue; + const CodeGenIntrinsic &intrinsic = *(I->first); // Keep track of the number of attributes we're writing out. unsigned numAttrs = 0; @@ -554,8 +568,8 @@ EmitAttributes(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS) { // The argument attributes are alreadys sorted by argument index. for (unsigned ai = 0, ae = intrinsic.ArgumentAttributes.size(); ai != ae;) { unsigned argNo = intrinsic.ArgumentAttributes[ai].first; - - OS << " AWI[" << numAttrs++ << "] = AttributeWithIndex::get(" + + OS << " AWI[" << numAttrs++ << "] = AttributeWithIndex::get(" << argNo+1 << ", "; bool moreThanOne = false; @@ -579,7 +593,7 @@ EmitAttributes(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS) { ModRefKind modRef = getModRefKind(intrinsic); if (!intrinsic.canThrow || modRef) { - OS << " AWI[" << numAttrs++ << "] = AttributeWithIndex::get(~0, "; + OS << " AWI[" << numAttrs++ << "] = AttributeWithIndex::get(~0, "; if (!intrinsic.canThrow) { OS << "Attribute::NoUnwind"; if (modRef) OS << '|'; @@ -593,13 +607,14 @@ EmitAttributes(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS) { } if (numAttrs) { - OS << " NumAttrs = " << numAttrs << ";\n"; - OS << " break;\n"; + OS << " NumAttrs = " << numAttrs << ";\n"; + OS << " break;\n"; } else { - OS << " return AttrListPtr();\n"; + OS << " return AttrListPtr();\n"; } } + OS << " }\n"; OS << " }\n"; OS << " return AttrListPtr::get(AWI, NumAttrs);\n"; OS << "}\n"; @@ -609,50 +624,36 @@ EmitAttributes(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS) { /// EmitModRefBehavior - Determine intrinsic alias analysis mod/ref behavior. void IntrinsicEmitter:: EmitModRefBehavior(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS){ - OS << "// Determine intrinsic alias analysis mod/ref behavior.\n"; - OS << "#ifdef GET_INTRINSIC_MODREF_BEHAVIOR\n"; - OS << "switch (iid) {\n"; - OS << "default:\n return UnknownModRefBehavior;\n"; + OS << "// Determine intrinsic alias analysis mod/ref behavior.\n" + << "#ifdef GET_INTRINSIC_MODREF_BEHAVIOR\n" + << "assert(iid <= Intrinsic::" << Ints.back().EnumName << " && " + << "\"Unknown intrinsic.\");\n\n"; + + OS << "static const uint8_t IntrinsicModRefBehavior[] = {\n" + << " /* invalid */ UnknownModRefBehavior,\n"; for (unsigned i = 0, e = Ints.size(); i != e; ++i) { - if (Ints[i].ModRef == CodeGenIntrinsic::ReadWriteMem) - continue; - OS << "case " << TargetPrefix << "Intrinsic::" << Ints[i].EnumName - << ":\n"; + OS << " /* " << TargetPrefix << Ints[i].EnumName << " */ "; switch (Ints[i].ModRef) { - default: - assert(false && "Unknown Mod/Ref type!"); case CodeGenIntrinsic::NoMem: - OS << " return DoesNotAccessMemory;\n"; + OS << "DoesNotAccessMemory,\n"; break; case CodeGenIntrinsic::ReadArgMem: - OS << " return OnlyReadsArgumentPointees;\n"; + OS << "OnlyReadsArgumentPointees,\n"; break; case CodeGenIntrinsic::ReadMem: - OS << " return OnlyReadsMemory;\n"; + OS << "OnlyReadsMemory,\n"; break; case CodeGenIntrinsic::ReadWriteArgMem: - OS << " return OnlyAccessesArgumentPointees;\n"; + OS << "OnlyAccessesArgumentPointees,\n"; + break; + case CodeGenIntrinsic::ReadWriteMem: + OS << "UnknownModRefBehavior,\n"; break; } } - OS << "}\n"; - OS << "#endif // GET_INTRINSIC_MODREF_BEHAVIOR\n\n"; -} - -void IntrinsicEmitter:: -EmitGCCBuiltinList(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS){ - OS << "// Get the GCC builtin that corresponds to an LLVM intrinsic.\n"; - OS << "#ifdef GET_GCC_BUILTIN_NAME\n"; - OS << " switch (F->getIntrinsicID()) {\n"; - OS << " default: BuiltinName = \"\"; break;\n"; - for (unsigned i = 0, e = Ints.size(); i != e; ++i) { - if (!Ints[i].GCCBuiltinName.empty()) { - OS << " case Intrinsic::" << Ints[i].EnumName << ": BuiltinName = \"" - << Ints[i].GCCBuiltinName << "\"; break;\n"; - } - } - OS << " }\n"; - OS << "#endif\n\n"; + OS << "};\n\n" + << "return static_cast<ModRefBehavior>(IntrinsicModRefBehavior[iid]);\n" + << "#endif // GET_INTRINSIC_MODREF_BEHAVIOR\n\n"; } /// EmitTargetBuiltins - All of the builtins in the specified map are for the diff --git a/utils/TableGen/IntrinsicEmitter.h b/utils/TableGen/IntrinsicEmitter.h index eb6379c..f9bcd59 100644 --- a/utils/TableGen/IntrinsicEmitter.h +++ b/utils/TableGen/IntrinsicEmitter.h @@ -48,8 +48,6 @@ namespace llvm { raw_ostream &OS); void EmitModRefBehavior(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS); - void EmitGCCBuiltinList(const std::vector<CodeGenIntrinsic> &Ints, - raw_ostream &OS); void EmitIntrinsicToGCCBuiltinMap(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS); void EmitSuffix(raw_ostream &OS); diff --git a/utils/TableGen/LLVMBuild.txt b/utils/TableGen/LLVMBuild.txt new file mode 100644 index 0000000..b0081eb --- /dev/null +++ b/utils/TableGen/LLVMBuild.txt @@ -0,0 +1,22 @@ +;===- ./utils/TableGen/LLVMBuild.txt ---------------------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = BuildTool +name = tblgen +parent = BuildTools +required_libraries = Support TableGen diff --git a/utils/TableGen/PseudoLoweringEmitter.cpp b/utils/TableGen/PseudoLoweringEmitter.cpp index c685527..802d112 100644 --- a/utils/TableGen/PseudoLoweringEmitter.cpp +++ b/utils/TableGen/PseudoLoweringEmitter.cpp @@ -67,7 +67,7 @@ addDagOperandMapping(Record *Rec, DagInit *Dag, CodeGenInstruction &Insn, // Since we added more than one, we also need to adjust the base. BaseIdx += NewOps - 1; } else - assert(0 && "Unhandled pseudo-expansion argument type!"); + llvm_unreachable("Unhandled pseudo-expansion argument type!"); } return OpsAdded; } @@ -100,8 +100,11 @@ void PseudoLoweringEmitter::evaluateExpansion(Record *Rec) { throw TGError(Rec->getLoc(), "Pseudo result '" + Operator->getName() + "' operand count mismatch"); + unsigned NumMIOperands = 0; + for (unsigned i = 0, e = Insn.Operands.size(); i != e; ++i) + NumMIOperands += Insn.Operands[i].MINumOperands; IndexedMap<OpData> OperandMap; - OperandMap.grow(Insn.Operands.size()); + OperandMap.grow(NumMIOperands); addDagOperandMapping(Rec, Dag, Insn, OperandMap, 0); @@ -176,8 +179,6 @@ void PseudoLoweringEmitter::emitLoweringEmitter(raw_ostream &o) { for (unsigned i = 0, e = Dest.Operands[OpNo].MINumOperands; i != e; ++i) { switch (Expansion.OperandMap[MIOpNo + i].Kind) { - default: - llvm_unreachable("Unknown operand type?!"); case OpData::Operand: o << " lowerOperand(MI->getOperand(" << Source.Operands[Expansion.OperandMap[MIOpNo].Data diff --git a/utils/TableGen/RegisterInfoEmitter.cpp b/utils/TableGen/RegisterInfoEmitter.cpp index b0f4ffc..a2478a7 100644 --- a/utils/TableGen/RegisterInfoEmitter.cpp +++ b/utils/TableGen/RegisterInfoEmitter.cpp @@ -16,6 +16,7 @@ #include "RegisterInfoEmitter.h" #include "CodeGenTarget.h" #include "CodeGenRegisters.h" +#include "SequenceToOffsetTable.h" #include "llvm/TableGen/Record.h" #include "llvm/ADT/BitVector.h" #include "llvm/ADT/StringExtras.h" @@ -31,6 +32,9 @@ RegisterInfoEmitter::runEnums(raw_ostream &OS, CodeGenTarget &Target, CodeGenRegBank &Bank) { const std::vector<CodeGenRegister*> &Registers = Bank.getRegisters(); + // Register enums are stored as uint16_t in the tables. Make sure we'll fit + assert(Registers.size() <= 0xffff && "Too many regs to fit in tables"); + std::string Namespace = Registers[0]->TheDef->getValueAsString("Namespace"); EmitSourceFileHeader("Target Register Enum Values", OS); @@ -41,7 +45,8 @@ RegisterInfoEmitter::runEnums(raw_ostream &OS, OS << "namespace llvm {\n\n"; OS << "class MCRegisterClass;\n" - << "extern MCRegisterClass " << Namespace << "MCRegisterClasses[];\n\n"; + << "extern const MCRegisterClass " << Namespace + << "MCRegisterClasses[];\n\n"; if (!Namespace.empty()) OS << "namespace " << Namespace << " {\n"; @@ -59,6 +64,11 @@ RegisterInfoEmitter::runEnums(raw_ostream &OS, ArrayRef<CodeGenRegisterClass*> RegisterClasses = Bank.getRegClasses(); if (!RegisterClasses.empty()) { + + // RegisterClass enums are stored as uint16_t in the tables. + assert(RegisterClasses.size() <= 0xffff && + "Too many register classes to fit in tables"); + OS << "\n// Register classes\n"; if (!Namespace.empty()) OS << "namespace " << Namespace << " {\n"; @@ -89,16 +99,104 @@ RegisterInfoEmitter::runEnums(raw_ostream &OS, OS << "}\n"; } + ArrayRef<CodeGenSubRegIndex*> SubRegIndices = Bank.getSubRegIndices(); + if (!SubRegIndices.empty()) { + OS << "\n// Subregister indices\n"; + std::string Namespace = + SubRegIndices[0]->getNamespace(); + if (!Namespace.empty()) + OS << "namespace " << Namespace << " {\n"; + OS << "enum {\n NoSubRegister,\n"; + for (unsigned i = 0, e = Bank.getNumNamedIndices(); i != e; ++i) + OS << " " << SubRegIndices[i]->getName() << ",\t// " << i+1 << "\n"; + OS << " NUM_TARGET_NAMED_SUBREGS\n};\n"; + if (!Namespace.empty()) + OS << "}\n"; + } OS << "} // End llvm namespace \n"; OS << "#endif // GET_REGINFO_ENUM\n\n"; } -void -RegisterInfoEmitter::EmitRegMapping(raw_ostream &OS, - const std::vector<CodeGenRegister*> &Regs, - bool isCtor) { +void RegisterInfoEmitter:: +EmitRegUnitPressure(raw_ostream &OS, const CodeGenRegBank &RegBank, + const std::string &ClassName) { + unsigned NumRCs = RegBank.getRegClasses().size(); + unsigned NumSets = RegBank.getNumRegPressureSets(); + + OS << "/// Get the weight in units of pressure for this register class.\n" + << "const RegClassWeight &" << ClassName << "::\n" + << "getRegClassWeight(const TargetRegisterClass *RC) const {\n" + << " static const RegClassWeight RCWeightTable[] = {\n"; + for (unsigned i = 0, e = NumRCs; i != e; ++i) { + const CodeGenRegisterClass &RC = *RegBank.getRegClasses()[i]; + const CodeGenRegister::Set &Regs = RC.getMembers(); + if (Regs.empty()) + OS << " {0, 0"; + else { + std::vector<unsigned> RegUnits; + RC.buildRegUnitSet(RegUnits); + OS << " {" << (*Regs.begin())->getWeight(RegBank) + << ", " << RegBank.getRegUnitSetWeight(RegUnits); + } + OS << "}, \t// " << RC.getName() << "\n"; + } + OS << " {0, 0} };\n" + << " return RCWeightTable[RC->getID()];\n" + << "}\n\n"; + + OS << "\n" + << "// Get the number of dimensions of register pressure.\n" + << "unsigned " << ClassName << "::getNumRegPressureSets() const {\n" + << " return " << NumSets << ";\n}\n\n"; + + OS << "// Get the register unit pressure limit for this dimension.\n" + << "// This limit must be adjusted dynamically for reserved registers.\n" + << "unsigned " << ClassName << "::\n" + << "getRegPressureSetLimit(unsigned Idx) const {\n" + << " static const unsigned PressureLimitTable[] = {\n"; + for (unsigned i = 0; i < NumSets; ++i ) { + const RegUnitSet &RegUnits = RegBank.getRegPressureSet(i); + OS << " " << RegBank.getRegUnitSetWeight(RegUnits.Units) + << ", \t// " << i << ": " << RegBank.getRegPressureSet(i).Name << "\n"; + } + OS << " 0 };\n" + << " return PressureLimitTable[Idx];\n" + << "}\n\n"; + + OS << "/// Get the dimensions of register pressure " + << "impacted by this register class.\n" + << "/// Returns a -1 terminated array of pressure set IDs\n" + << "const int* " << ClassName << "::\n" + << "getRegClassPressureSets(const TargetRegisterClass *RC) const {\n" + << " static const int RCSetsTable[] = {\n "; + std::vector<unsigned> RCSetStarts(NumRCs); + for (unsigned i = 0, StartIdx = 0, e = NumRCs; i != e; ++i) { + RCSetStarts[i] = StartIdx; + ArrayRef<unsigned> PSetIDs = RegBank.getRCPressureSetIDs(i); + for (ArrayRef<unsigned>::iterator PSetI = PSetIDs.begin(), + PSetE = PSetIDs.end(); PSetI != PSetE; ++PSetI) { + OS << *PSetI << ", "; + ++StartIdx; + } + OS << "-1, \t// " << RegBank.getRegClasses()[i]->getName() << "\n "; + ++StartIdx; + } + OS << "-1 };\n"; + OS << " static const unsigned RCSetStartTable[] = {\n "; + for (unsigned i = 0, e = NumRCs; i != e; ++i) { + OS << RCSetStarts[i] << ","; + } + OS << "0 };\n" + << " unsigned SetListStart = RCSetStartTable[RC->getID()];\n" + << " return &RCSetsTable[SetListStart];\n" + << "}\n\n"; +} +void +RegisterInfoEmitter::EmitRegMappingTables(raw_ostream &OS, + const std::vector<CodeGenRegister*> &Regs, + bool isCtor) { // Collect all information about dwarf register numbers typedef std::map<Record*, std::vector<int64_t>, LessRecord> DwarfRegNumsMapTy; DwarfRegNumsMapTy DwarfRegNums; @@ -124,6 +222,121 @@ RegisterInfoEmitter::EmitRegMapping(raw_ostream &OS, for (unsigned i = I->second.size(), e = maxLength; i != e; ++i) I->second.push_back(-1); + std::string Namespace = Regs[0]->TheDef->getValueAsString("Namespace"); + + OS << "// " << Namespace << " Dwarf<->LLVM register mappings.\n"; + + // Emit reverse information about the dwarf register numbers. + for (unsigned j = 0; j < 2; ++j) { + for (unsigned i = 0, e = maxLength; i != e; ++i) { + OS << "extern const MCRegisterInfo::DwarfLLVMRegPair " << Namespace; + OS << (j == 0 ? "DwarfFlavour" : "EHFlavour"); + OS << i << "Dwarf2L[]"; + + if (!isCtor) { + OS << " = {\n"; + + // Store the mapping sorted by the LLVM reg num so lookup can be done + // with a binary search. + std::map<uint64_t, Record*> Dwarf2LMap; + for (DwarfRegNumsMapTy::iterator + I = DwarfRegNums.begin(), E = DwarfRegNums.end(); I != E; ++I) { + int DwarfRegNo = I->second[i]; + if (DwarfRegNo < 0) + continue; + Dwarf2LMap[DwarfRegNo] = I->first; + } + + for (std::map<uint64_t, Record*>::iterator + I = Dwarf2LMap.begin(), E = Dwarf2LMap.end(); I != E; ++I) + OS << " { " << I->first << "U, " << getQualifiedName(I->second) + << " },\n"; + + OS << "};\n"; + } else { + OS << ";\n"; + } + + // We have to store the size in a const global, it's used in multiple + // places. + OS << "extern const unsigned " << Namespace + << (j == 0 ? "DwarfFlavour" : "EHFlavour") << i << "Dwarf2LSize"; + if (!isCtor) + OS << " = sizeof(" << Namespace + << (j == 0 ? "DwarfFlavour" : "EHFlavour") << i + << "Dwarf2L)/sizeof(MCRegisterInfo::DwarfLLVMRegPair);\n\n"; + else + OS << ";\n\n"; + } + } + + for (unsigned i = 0, e = Regs.size(); i != e; ++i) { + Record *Reg = Regs[i]->TheDef; + const RecordVal *V = Reg->getValue("DwarfAlias"); + if (!V || !V->getValue()) + continue; + + DefInit *DI = dynamic_cast<DefInit*>(V->getValue()); + Record *Alias = DI->getDef(); + DwarfRegNums[Reg] = DwarfRegNums[Alias]; + } + + // Emit information about the dwarf register numbers. + for (unsigned j = 0; j < 2; ++j) { + for (unsigned i = 0, e = maxLength; i != e; ++i) { + OS << "extern const MCRegisterInfo::DwarfLLVMRegPair " << Namespace; + OS << (j == 0 ? "DwarfFlavour" : "EHFlavour"); + OS << i << "L2Dwarf[]"; + if (!isCtor) { + OS << " = {\n"; + // Store the mapping sorted by the Dwarf reg num so lookup can be done + // with a binary search. + for (DwarfRegNumsMapTy::iterator + I = DwarfRegNums.begin(), E = DwarfRegNums.end(); I != E; ++I) { + int RegNo = I->second[i]; + if (RegNo == -1) // -1 is the default value, don't emit a mapping. + continue; + + OS << " { " << getQualifiedName(I->first) << ", " << RegNo + << "U },\n"; + } + OS << "};\n"; + } else { + OS << ";\n"; + } + + // We have to store the size in a const global, it's used in multiple + // places. + OS << "extern const unsigned " << Namespace + << (j == 0 ? "DwarfFlavour" : "EHFlavour") << i << "L2DwarfSize"; + if (!isCtor) + OS << " = sizeof(" << Namespace + << (j == 0 ? "DwarfFlavour" : "EHFlavour") << i + << "L2Dwarf)/sizeof(MCRegisterInfo::DwarfLLVMRegPair);\n\n"; + else + OS << ";\n\n"; + } + } +} + +void +RegisterInfoEmitter::EmitRegMapping(raw_ostream &OS, + const std::vector<CodeGenRegister*> &Regs, + bool isCtor) { + // Emit the initializer so the tables from EmitRegMappingTables get wired up + // to the MCRegisterInfo object. + unsigned maxLength = 0; + for (unsigned i = 0, e = Regs.size(); i != e; ++i) { + Record *Reg = Regs[i]->TheDef; + maxLength = std::max((size_t)maxLength, + Reg->getValueAsListOfInts("DwarfNumbers").size()); + } + + if (!maxLength) + return; + + std::string Namespace = Regs[0]->TheDef->getValueAsString("Namespace"); + // Emit reverse information about the dwarf register numbers. for (unsigned j = 0; j < 2; ++j) { OS << " switch ("; @@ -133,43 +346,28 @@ RegisterInfoEmitter::EmitRegMapping(raw_ostream &OS, OS << "EHFlavour"; OS << ") {\n" << " default:\n" - << " assert(0 && \"Unknown DWARF flavour\");\n" - << " break;\n"; + << " llvm_unreachable(\"Unknown DWARF flavour\");\n"; for (unsigned i = 0, e = maxLength; i != e; ++i) { OS << " case " << i << ":\n"; - for (DwarfRegNumsMapTy::iterator - I = DwarfRegNums.begin(), E = DwarfRegNums.end(); I != E; ++I) { - int DwarfRegNo = I->second[i]; - if (DwarfRegNo < 0) - continue; - OS << " "; - if (!isCtor) - OS << "RI->"; - OS << "mapDwarfRegToLLVMReg(" << DwarfRegNo << ", " - << getQualifiedName(I->first) << ", "; - if (j == 0) + OS << " "; + if (!isCtor) + OS << "RI->"; + std::string Tmp; + raw_string_ostream(Tmp) << Namespace + << (j == 0 ? "DwarfFlavour" : "EHFlavour") << i + << "Dwarf2L"; + OS << "mapDwarfRegsToLLVMRegs(" << Tmp << ", " << Tmp << "Size, "; + if (j == 0) OS << "false"; else OS << "true"; - OS << " );\n"; - } + OS << ");\n"; OS << " break;\n"; } OS << " }\n"; } - for (unsigned i = 0, e = Regs.size(); i != e; ++i) { - Record *Reg = Regs[i]->TheDef; - const RecordVal *V = Reg->getValue("DwarfAlias"); - if (!V || !V->getValue()) - continue; - - DefInit *DI = dynamic_cast<DefInit*>(V->getValue()); - Record *Alias = DI->getDef(); - DwarfRegNums[Reg] = DwarfRegNums[Alias]; - } - // Emit information about the dwarf register numbers. for (unsigned j = 0; j < 2; ++j) { OS << " switch ("; @@ -179,26 +377,23 @@ RegisterInfoEmitter::EmitRegMapping(raw_ostream &OS, OS << "EHFlavour"; OS << ") {\n" << " default:\n" - << " assert(0 && \"Unknown DWARF flavour\");\n" - << " break;\n"; + << " llvm_unreachable(\"Unknown DWARF flavour\");\n"; for (unsigned i = 0, e = maxLength; i != e; ++i) { OS << " case " << i << ":\n"; - // Sort by name to get a stable order. - for (DwarfRegNumsMapTy::iterator - I = DwarfRegNums.begin(), E = DwarfRegNums.end(); I != E; ++I) { - int RegNo = I->second[i]; - OS << " "; - if (!isCtor) - OS << "RI->"; - OS << "mapLLVMRegToDwarfReg(" << getQualifiedName(I->first) << ", " - << RegNo << ", "; - if (j == 0) + OS << " "; + if (!isCtor) + OS << "RI->"; + std::string Tmp; + raw_string_ostream(Tmp) << Namespace + << (j == 0 ? "DwarfFlavour" : "EHFlavour") << i + << "L2Dwarf"; + OS << "mapLLVMRegsToDwarfRegs(" << Tmp << ", " << Tmp << "Size, "; + if (j == 0) OS << "false"; else OS << "true"; - OS << " );\n"; - } + OS << ");\n"; OS << " break;\n"; } OS << " }\n"; @@ -235,6 +430,14 @@ public: } }; +static void printRegister(raw_ostream &OS, const CodeGenRegister *Reg) { + OS << getQualifiedName(Reg->TheDef); +} + +static void printSimpleValueType(raw_ostream &OS, MVT::SimpleValueType VT) { + OS << getEnumName(VT); +} + // // runMCDesc - Print out MC register descriptions. // @@ -246,87 +449,79 @@ RegisterInfoEmitter::runMCDesc(raw_ostream &OS, CodeGenTarget &Target, OS << "\n#ifdef GET_REGINFO_MC_DESC\n"; OS << "#undef GET_REGINFO_MC_DESC\n"; + const std::vector<CodeGenRegister*> &Regs = RegBank.getRegisters(); std::map<const CodeGenRegister*, CodeGenRegister::Set> Overlaps; RegBank.computeOverlaps(Overlaps); - OS << "namespace llvm {\n\n"; - - const std::string &TargetName = Target.getName(); - std::string ClassName = TargetName + "GenMCRegisterInfo"; - OS << "struct " << ClassName << " : public MCRegisterInfo {\n" - << " explicit " << ClassName << "(const MCRegisterDesc *D);\n"; - OS << "};\n"; + // The lists of sub-registers, super-registers, and overlaps all go in the + // same array. That allows us to share suffixes. + typedef std::vector<const CodeGenRegister*> RegVec; + SmallVector<RegVec, 4> SubRegLists(Regs.size()); + SmallVector<RegVec, 4> OverlapLists(Regs.size()); + SequenceToOffsetTable<RegVec, CodeGenRegister::Less> RegSeqs; - OS << "\nnamespace {\n"; - - const std::vector<CodeGenRegister*> &Regs = RegBank.getRegisters(); - - // Emit an overlap list for all registers. + // Precompute register lists for the SequenceToOffsetTable. for (unsigned i = 0, e = Regs.size(); i != e; ++i) { const CodeGenRegister *Reg = Regs[i]; - const CodeGenRegister::Set &O = Overlaps[Reg]; - // Move Reg to the front so TRI::getAliasSet can share the list. - OS << " const unsigned " << Reg->getName() << "_Overlaps[] = { " - << getQualifiedName(Reg->TheDef) << ", "; - for (CodeGenRegister::Set::const_iterator I = O.begin(), E = O.end(); - I != E; ++I) - if (*I != Reg) - OS << getQualifiedName((*I)->TheDef) << ", "; - OS << "0 };\n"; - } - // Emit the empty sub-registers list - OS << " const unsigned Empty_SubRegsSet[] = { 0 };\n"; - // Loop over all of the registers which have sub-registers, emitting the - // sub-registers list to memory. - for (unsigned i = 0, e = Regs.size(); i != e; ++i) { - const CodeGenRegister &Reg = *Regs[i]; - if (Reg.getSubRegs().empty()) - continue; - // getSubRegs() orders by SubRegIndex. We want a topological order. - SetVector<CodeGenRegister*> SR; - Reg.addSubRegsPreOrder(SR); - OS << " const unsigned " << Reg.getName() << "_SubRegsSet[] = { "; - for (unsigned j = 0, je = SR.size(); j != je; ++j) - OS << getQualifiedName(SR[j]->TheDef) << ", "; - OS << "0 };\n"; + // Compute the ordered sub-register list. + SetVector<const CodeGenRegister*> SR; + Reg->addSubRegsPreOrder(SR, RegBank); + RegVec &SubRegList = SubRegLists[i]; + SubRegList.assign(SR.begin(), SR.end()); + RegSeqs.add(SubRegList); + + // Super-registers are already computed. + const RegVec &SuperRegList = Reg->getSuperRegs(); + RegSeqs.add(SuperRegList); + + // The list of overlaps doesn't need to have any particular order, except + // Reg itself must be the first element. Pick an ordering that has one of + // the other lists as a suffix. + RegVec &OverlapList = OverlapLists[i]; + const RegVec &Suffix = SubRegList.size() > SuperRegList.size() ? + SubRegList : SuperRegList; + CodeGenRegister::Set Omit(Suffix.begin(), Suffix.end()); + + // First element is Reg itself. + OverlapList.push_back(Reg); + Omit.insert(Reg); + + // Any elements not in Suffix. + const CodeGenRegister::Set &OSet = Overlaps[Reg]; + std::set_difference(OSet.begin(), OSet.end(), + Omit.begin(), Omit.end(), + std::back_inserter(OverlapList), + CodeGenRegister::Less()); + + // Finally, Suffix itself. + OverlapList.insert(OverlapList.end(), Suffix.begin(), Suffix.end()); + RegSeqs.add(OverlapList); } - // Emit the empty super-registers list - OS << " const unsigned Empty_SuperRegsSet[] = { 0 };\n"; - // Loop over all of the registers which have super-registers, emitting the - // super-registers list to memory. - for (unsigned i = 0, e = Regs.size(); i != e; ++i) { - const CodeGenRegister &Reg = *Regs[i]; - const CodeGenRegister::SuperRegList &SR = Reg.getSuperRegs(); - if (SR.empty()) - continue; - OS << " const unsigned " << Reg.getName() << "_SuperRegsSet[] = { "; - for (unsigned j = 0, je = SR.size(); j != je; ++j) - OS << getQualifiedName(SR[j]->TheDef) << ", "; - OS << "0 };\n"; - } - OS << "}\n"; // End of anonymous namespace... + // Compute the final layout of the sequence table. + RegSeqs.layout(); - OS << "\nMCRegisterDesc " << TargetName + OS << "namespace llvm {\n\n"; + + const std::string &TargetName = Target.getName(); + + // Emit the shared table of register lists. + OS << "extern const uint16_t " << TargetName << "RegLists[] = {\n"; + RegSeqs.emit(OS, printRegister); + OS << "};\n\n"; + + OS << "extern const MCRegisterDesc " << TargetName << "RegDesc[] = { // Descriptors\n"; - OS << " { \"NOREG\",\t0,\t0,\t0 },\n"; + OS << " { \"NOREG\", 0, 0, 0 },\n"; - // Now that register alias and sub-registers sets have been emitted, emit the - // register descriptors now. + // Emit the register descriptors now. for (unsigned i = 0, e = Regs.size(); i != e; ++i) { - const CodeGenRegister &Reg = *Regs[i]; - OS << " { \""; - OS << Reg.getName() << "\",\t" << Reg.getName() << "_Overlaps,\t"; - if (!Reg.getSubRegs().empty()) - OS << Reg.getName() << "_SubRegsSet,\t"; - else - OS << "Empty_SubRegsSet,\t"; - if (!Reg.getSuperRegs().empty()) - OS << Reg.getName() << "_SuperRegsSet"; - else - OS << "Empty_SuperRegsSet"; - OS << " },\n"; + const CodeGenRegister *Reg = Regs[i]; + OS << " { \"" << Reg->getName() << "\", " + << RegSeqs.get(OverlapLists[i]) << ", " + << RegSeqs.get(SubRegLists[i]) << ", " + << RegSeqs.get(Reg->getSuperRegs()) << " },\n"; } OS << "};\n\n"; // End of register descriptors... @@ -345,7 +540,7 @@ RegisterInfoEmitter::runMCDesc(raw_ostream &OS, CodeGenTarget &Target, // Emit the register list now. OS << " // " << Name << " Register Class...\n" - << " static const unsigned " << Name + << " const uint16_t " << Name << "[] = {\n "; for (unsigned i = 0, e = Order.size(); i != e; ++i) { Record *Reg = Order[i]; @@ -354,7 +549,7 @@ RegisterInfoEmitter::runMCDesc(raw_ostream &OS, CodeGenTarget &Target, OS << "\n };\n\n"; OS << " // " << Name << " Bit set.\n" - << " static const unsigned char " << Name + << " const uint8_t " << Name << "Bits[] = {\n "; BitVectorEmitter BVE; for (unsigned i = 0, e = Order.size(); i != e; ++i) { @@ -367,37 +562,81 @@ RegisterInfoEmitter::runMCDesc(raw_ostream &OS, CodeGenTarget &Target, } OS << "}\n\n"; - OS << "MCRegisterClass " << TargetName << "MCRegisterClasses[] = {\n"; + OS << "extern const MCRegisterClass " << TargetName + << "MCRegisterClasses[] = {\n"; for (unsigned rc = 0, e = RegisterClasses.size(); rc != e; ++rc) { const CodeGenRegisterClass &RC = *RegisterClasses[rc]; - OS << " MCRegisterClass(" << RC.getQualifiedName() + "RegClassID" << ", " - << '\"' << RC.getName() << "\", " + + // Asserts to make sure values will fit in table assuming types from + // MCRegisterInfo.h + assert((RC.SpillSize/8) <= 0xffff && "SpillSize too large."); + assert((RC.SpillAlignment/8) <= 0xffff && "SpillAlignment too large."); + assert(RC.CopyCost >= -128 && RC.CopyCost <= 127 && "Copy cost too large."); + + OS << " { " << '\"' << RC.getName() << "\", " + << RC.getName() << ", " << RC.getName() << "Bits, " + << RC.getOrder().size() << ", sizeof(" << RC.getName() << "Bits), " + << RC.getQualifiedName() + "RegClassID" << ", " << RC.SpillSize/8 << ", " << RC.SpillAlignment/8 << ", " << RC.CopyCost << ", " - << RC.Allocatable << ", " - << RC.getName() << ", " << RC.getName() << " + " - << RC.getOrder().size() << ", " - << RC.getName() << "Bits, sizeof(" << RC.getName() << "Bits)" - << "),\n"; + << RC.Allocatable << " },\n"; } OS << "};\n\n"; + // Emit the data table for getSubReg(). + ArrayRef<CodeGenSubRegIndex*> SubRegIndices = RegBank.getSubRegIndices(); + if (SubRegIndices.size()) { + OS << "const uint16_t " << TargetName << "SubRegTable[][" + << SubRegIndices.size() << "] = {\n"; + for (unsigned i = 0, e = Regs.size(); i != e; ++i) { + const CodeGenRegister::SubRegMap &SRM = Regs[i]->getSubRegs(); + OS << " /* " << Regs[i]->TheDef->getName() << " */\n"; + if (SRM.empty()) { + OS << " {0},\n"; + continue; + } + OS << " {"; + for (unsigned j = 0, je = SubRegIndices.size(); j != je; ++j) { + // FIXME: We really should keep this to 80 columns... + CodeGenRegister::SubRegMap::const_iterator SubReg = + SRM.find(SubRegIndices[j]); + if (SubReg != SRM.end()) + OS << getQualifiedName(SubReg->second->TheDef); + else + OS << "0"; + if (j != je - 1) + OS << ", "; + } + OS << "}" << (i != e ? "," : "") << "\n"; + } + OS << "};\n\n"; + OS << "const uint16_t *get" << TargetName + << "SubRegTable() {\n return (const uint16_t *)" << TargetName + << "SubRegTable;\n}\n\n"; + } + + EmitRegMappingTables(OS, Regs, false); + // MCRegisterInfo initialization routine. OS << "static inline void Init" << TargetName << "MCRegisterInfo(MCRegisterInfo *RI, unsigned RA, " << "unsigned DwarfFlavour = 0, unsigned EHFlavour = 0) {\n"; OS << " RI->InitMCRegisterInfo(" << TargetName << "RegDesc, " << Regs.size()+1 << ", RA, " << TargetName << "MCRegisterClasses, " - << RegisterClasses.size() << ");\n\n"; + << RegisterClasses.size() << ", " << TargetName << "RegLists, "; + if (SubRegIndices.size() != 0) + OS << "(uint16_t*)" << TargetName << "SubRegTable, " + << SubRegIndices.size() << ");\n\n"; + else + OS << "NULL, 0);\n\n"; EmitRegMapping(OS, Regs, false); OS << "}\n\n"; - OS << "} // End llvm namespace \n"; OS << "#endif // GET_REGINFO_MC_DESC\n\n"; } @@ -413,8 +652,7 @@ RegisterInfoEmitter::runTargetHeader(raw_ostream &OS, CodeGenTarget &Target, const std::string &TargetName = Target.getName(); std::string ClassName = TargetName + "GenRegisterInfo"; - OS << "#include \"llvm/Target/TargetRegisterInfo.h\"\n"; - OS << "#include <string>\n\n"; + OS << "#include \"llvm/Target/TargetRegisterInfo.h\"\n\n"; OS << "namespace llvm {\n\n"; @@ -423,28 +661,20 @@ RegisterInfoEmitter::runTargetHeader(raw_ostream &OS, CodeGenTarget &Target, << "(unsigned RA, unsigned D = 0, unsigned E = 0);\n" << " virtual bool needsStackRealignment(const MachineFunction &) const\n" << " { return false; }\n" - << " unsigned getSubReg(unsigned RegNo, unsigned Index) const;\n" - << " unsigned getSubRegIndex(unsigned RegNo, unsigned SubRegNo) const;\n" << " unsigned composeSubRegIndices(unsigned, unsigned) const;\n" << " const TargetRegisterClass *" "getSubClassWithSubReg(const TargetRegisterClass*, unsigned) const;\n" + << " const TargetRegisterClass *getMatchingSuperRegClass(" + "const TargetRegisterClass*, const TargetRegisterClass*, " + "unsigned) const;\n" + << " const RegClassWeight &getRegClassWeight(" + << "const TargetRegisterClass *RC) const;\n" + << " unsigned getNumRegPressureSets() const;\n" + << " unsigned getRegPressureSetLimit(unsigned Idx) const;\n" + << " const int *getRegClassPressureSets(" + << "const TargetRegisterClass *RC) const;\n" << "};\n\n"; - const std::vector<Record*> &SubRegIndices = RegBank.getSubRegIndices(); - if (!SubRegIndices.empty()) { - OS << "\n// Subregister indices\n"; - std::string Namespace = SubRegIndices[0]->getValueAsString("Namespace"); - if (!Namespace.empty()) - OS << "namespace " << Namespace << " {\n"; - OS << "enum {\n NoSubRegister,\n"; - for (unsigned i = 0, e = RegBank.getNumNamedIndices(); i != e; ++i) - OS << " " << SubRegIndices[i]->getName() << ",\t// " << i+1 << "\n"; - OS << " NUM_TARGET_NAMED_SUBREGS = " << SubRegIndices.size()+1 << "\n"; - OS << "};\n"; - if (!Namespace.empty()) - OS << "}\n"; - } - ArrayRef<CodeGenRegisterClass*> RegisterClasses = RegBank.getRegClasses(); if (!RegisterClasses.empty()) { @@ -455,19 +685,11 @@ RegisterInfoEmitter::runTargetHeader(raw_ostream &OS, CodeGenTarget &Target, const CodeGenRegisterClass &RC = *RegisterClasses[i]; const std::string &Name = RC.getName(); - // Output the register class definition. - OS << " struct " << Name << "Class : public TargetRegisterClass {\n" - << " " << Name << "Class();\n"; - if (!RC.AltOrderSelect.empty()) - OS << " ArrayRef<unsigned> " - "getRawAllocationOrder(const MachineFunction&) const;\n"; - OS << " };\n"; - // Output the extern for the instance. - OS << " extern " << Name << "Class\t" << Name << "RegClass;\n"; + OS << " extern const TargetRegisterClass " << Name << "RegClass;\n"; // Output the extern for the pointer to the instance (should remove). - OS << " static TargetRegisterClass * const "<< Name <<"RegisterClass = &" - << Name << "RegClass;\n"; + OS << " static const TargetRegisterClass * const " << Name + << "RegisterClass = &" << Name << "RegClass;\n"; } OS << "} // end of namespace " << TargetName << "\n\n"; } @@ -489,8 +711,8 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target, OS << "namespace llvm {\n\n"; // Get access to MCRegisterClass data. - OS << "extern MCRegisterClass " << Target.getName() - << "MCRegisterClasses[];\n"; + OS << "extern const MCRegisterClass " << Target.getName() + << "MCRegisterClasses[];\n"; // Start out by emitting each of the register classes. ArrayRef<CodeGenRegisterClass*> RegisterClasses = RegBank.getRegClasses(); @@ -507,38 +729,21 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target, AllocatableRegs.insert(Order.begin(), Order.end()); } - OS << "namespace { // Register classes...\n"; - - // Emit the ValueType arrays for each RegisterClass - for (unsigned rc = 0, e = RegisterClasses.size(); rc != e; ++rc) { - const CodeGenRegisterClass &RC = *RegisterClasses[rc]; - - // Give the register class a legal C name if it's anonymous. - std::string Name = RC.getName() + "VTs"; - - // Emit the register list now. - OS << " // " << Name - << " Register Class Value Types...\n" - << " static const EVT " << Name - << "[] = {\n "; - for (unsigned i = 0, e = RC.VTs.size(); i != e; ++i) - OS << getEnumName(RC.VTs[i]) << ", "; - OS << "MVT::Other\n };\n\n"; - } - OS << "} // end anonymous namespace\n\n"; + // Build a shared array of value types. + SequenceToOffsetTable<std::vector<MVT::SimpleValueType> > VTSeqs; + for (unsigned rc = 0, e = RegisterClasses.size(); rc != e; ++rc) + VTSeqs.add(RegisterClasses[rc]->VTs); + VTSeqs.layout(); + OS << "\nstatic const MVT::SimpleValueType VTLists[] = {\n"; + VTSeqs.emit(OS, printSimpleValueType, "MVT::Other"); + OS << "};\n"; // Now that all of the structs have been emitted, emit the instances. if (!RegisterClasses.empty()) { - OS << "namespace " << RegisterClasses[0]->Namespace - << " { // Register class instances\n"; - for (unsigned i = 0, e = RegisterClasses.size(); i != e; ++i) - OS << " " << RegisterClasses[i]->getName() << "Class\t" - << RegisterClasses[i]->getName() << "RegClass;\n"; - std::map<unsigned, std::set<unsigned> > SuperRegClassMap; - OS << "\n static const TargetRegisterClass* const " - << "NullRegClasses[] = { NULL };\n\n"; + OS << "\nstatic const TargetRegisterClass *const " + << "NullRegClasses[] = { NULL };\n\n"; unsigned NumSubRegIndices = RegBank.getSubRegIndices().size(); @@ -563,10 +768,10 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target, // Give the register class a legal C name if it's anonymous. std::string Name = RC.getName(); - OS << " // " << Name + OS << "// " << Name << " Super-register Classes...\n" - << " static const TargetRegisterClass* const " - << Name << "SuperRegClasses[] = {\n "; + << "static const TargetRegisterClass *const " + << Name << "SuperRegClasses[] = {\n "; bool Empty = true; std::map<unsigned, std::set<unsigned> >::iterator I = @@ -583,7 +788,7 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target, } OS << (!Empty ? ", " : "") << "NULL"; - OS << "\n };\n\n"; + OS << "\n};\n\n"; } } @@ -594,9 +799,9 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target, // Give the register class a legal C name if it's anonymous. std::string Name = RC.getName(); - OS << " static const unsigned " << Name << "SubclassMask[] = { "; + OS << "static const uint32_t " << Name << "SubclassMask[] = {\n "; printBitVectorAsHex(OS, RC.getSubClasses(), 32); - OS << "};\n\n"; + OS << "\n};\n\n"; } // Emit NULL terminated super-class lists. @@ -608,54 +813,71 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target, if (Supers.empty()) continue; - OS << " static const TargetRegisterClass* const " + OS << "static const TargetRegisterClass *const " << RC.getName() << "Superclasses[] = {\n"; for (unsigned i = 0; i != Supers.size(); ++i) - OS << " &" << Supers[i]->getQualifiedName() << "RegClass,\n"; - OS << " NULL\n };\n\n"; + OS << " &" << Supers[i]->getQualifiedName() << "RegClass,\n"; + OS << " NULL\n};\n\n"; } // Emit methods. for (unsigned i = 0, e = RegisterClasses.size(); i != e; ++i) { const CodeGenRegisterClass &RC = *RegisterClasses[i]; - OS << RC.getName() << "Class::" << RC.getName() - << "Class() : TargetRegisterClass(&" - << Target.getName() << "MCRegisterClasses[" - << RC.getName() + "RegClassID" << "], " - << RC.getName() + "VTs" << ", " - << RC.getName() + "SubclassMask" << ", "; - if (RC.getSuperClasses().empty()) - OS << "NullRegClasses, "; - else - OS << RC.getName() + "Superclasses, "; - OS << (NumSubRegIndices ? RC.getName() + "Super" : std::string("Null")) - << "RegClasses" - << ") {}\n"; if (!RC.AltOrderSelect.empty()) { OS << "\nstatic inline unsigned " << RC.getName() << "AltOrderSelect(const MachineFunction &MF) {" - << RC.AltOrderSelect << "}\n\nArrayRef<unsigned> " - << RC.getName() << "Class::" - << "getRawAllocationOrder(const MachineFunction &MF) const {\n"; + << RC.AltOrderSelect << "}\n\n" + << "static ArrayRef<uint16_t> " << RC.getName() + << "GetRawAllocationOrder(const MachineFunction &MF) {\n"; for (unsigned oi = 1 , oe = RC.getNumOrders(); oi != oe; ++oi) { ArrayRef<Record*> Elems = RC.getOrder(oi); - OS << " static const unsigned AltOrder" << oi << "[] = {"; - for (unsigned elem = 0; elem != Elems.size(); ++elem) - OS << (elem ? ", " : " ") << getQualifiedName(Elems[elem]); - OS << " };\n"; + if (!Elems.empty()) { + OS << " static const uint16_t AltOrder" << oi << "[] = {"; + for (unsigned elem = 0; elem != Elems.size(); ++elem) + OS << (elem ? ", " : " ") << getQualifiedName(Elems[elem]); + OS << " };\n"; + } } OS << " const MCRegisterClass &MCR = " << Target.getName() - << "MCRegisterClasses[" << RC.getQualifiedName() + "RegClassID];" - << " static const ArrayRef<unsigned> Order[] = {\n" + << "MCRegisterClasses[" << RC.getQualifiedName() + "RegClassID];\n" + << " const ArrayRef<uint16_t> Order[] = {\n" << " makeArrayRef(MCR.begin(), MCR.getNumRegs()"; for (unsigned oi = 1, oe = RC.getNumOrders(); oi != oe; ++oi) - OS << "),\n makeArrayRef(AltOrder" << oi; + if (RC.getOrder(oi).empty()) + OS << "),\n ArrayRef<uint16_t>("; + else + OS << "),\n makeArrayRef(AltOrder" << oi; OS << ")\n };\n const unsigned Select = " << RC.getName() << "AltOrderSelect(MF);\n assert(Select < " << RC.getNumOrders() << ");\n return Order[Select];\n}\n"; } } + // Now emit the actual value-initialized register class instances. + OS << "namespace " << RegisterClasses[0]->Namespace + << " { // Register class instances\n"; + + for (unsigned i = 0, e = RegisterClasses.size(); i != e; ++i) { + const CodeGenRegisterClass &RC = *RegisterClasses[i]; + OS << " extern const TargetRegisterClass " + << RegisterClasses[i]->getName() << "RegClass = {\n " + << '&' << Target.getName() << "MCRegisterClasses[" << RC.getName() + << "RegClassID],\n " + << "VTLists + " << VTSeqs.get(RC.VTs) << ",\n " + << RC.getName() << "SubclassMask,\n "; + if (RC.getSuperClasses().empty()) + OS << "NullRegClasses,\n "; + else + OS << RC.getName() << "Superclasses,\n "; + OS << (NumSubRegIndices ? RC.getName() + "Super" : std::string("Null")) + << "RegClasses,\n "; + if (RC.AltOrderSelect.empty()) + OS << "0\n"; + else + OS << RC.getName() << "GetRawAllocationOrder\n"; + OS << " };\n\n"; + } + OS << "}\n"; } @@ -669,28 +891,27 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target, // Emit extra information about registers. const std::string &TargetName = Target.getName(); - OS << "\n static const TargetRegisterInfoDesc " - << TargetName << "RegInfoDesc[] = " - << "{ // Extra Descriptors\n"; - OS << " { 0, 0 },\n"; + OS << "\nstatic const TargetRegisterInfoDesc " + << TargetName << "RegInfoDesc[] = { // Extra Descriptors\n"; + OS << " { 0, 0 },\n"; const std::vector<CodeGenRegister*> &Regs = RegBank.getRegisters(); for (unsigned i = 0, e = Regs.size(); i != e; ++i) { const CodeGenRegister &Reg = *Regs[i]; - OS << " { "; + OS << " { "; OS << Reg.CostPerUse << ", " << int(AllocatableRegs.count(Reg.TheDef)) << " },\n"; } - OS << " };\n"; // End of register descriptors... + OS << "};\n"; // End of register descriptors... // Calculate the mapping of subregister+index pairs to physical registers. - // This will also create further anonymous indexes. + // This will also create further anonymous indices. unsigned NamedIndices = RegBank.getNumNamedIndices(); // Emit SubRegIndex names, skipping 0 - const std::vector<Record*> &SubRegIndices = RegBank.getSubRegIndices(); - OS << "\n static const char *const " << TargetName + ArrayRef<CodeGenSubRegIndex*> SubRegIndices = RegBank.getSubRegIndices(); + OS << "\nstatic const char *const " << TargetName << "SubRegIndexTable[] = { \""; for (unsigned i = 0, e = SubRegIndices.size(); i != e; ++i) { OS << SubRegIndices[i]->getName(); @@ -699,7 +920,7 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target, } OS << "\" };\n\n"; - // Emit names of the anonymus subreg indexes. + // Emit names of the anonymous subreg indices. if (SubRegIndices.size() > NamedIndices) { OS << " enum {"; for (unsigned i = NamedIndices, e = SubRegIndices.size(); i != e; ++i) { @@ -713,48 +934,6 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target, std::string ClassName = Target.getName() + "GenRegisterInfo"; - // Emit the subregister + index mapping function based on the information - // calculated above. - OS << "unsigned " << ClassName - << "::getSubReg(unsigned RegNo, unsigned Index) const {\n" - << " switch (RegNo) {\n" - << " default:\n return 0;\n"; - for (unsigned i = 0, e = Regs.size(); i != e; ++i) { - const CodeGenRegister::SubRegMap &SRM = Regs[i]->getSubRegs(); - if (SRM.empty()) - continue; - OS << " case " << getQualifiedName(Regs[i]->TheDef) << ":\n"; - OS << " switch (Index) {\n"; - OS << " default: return 0;\n"; - for (CodeGenRegister::SubRegMap::const_iterator ii = SRM.begin(), - ie = SRM.end(); ii != ie; ++ii) - OS << " case " << getQualifiedName(ii->first) - << ": return " << getQualifiedName(ii->second->TheDef) << ";\n"; - OS << " };\n" << " break;\n"; - } - OS << " };\n"; - OS << " return 0;\n"; - OS << "}\n\n"; - - OS << "unsigned " << ClassName - << "::getSubRegIndex(unsigned RegNo, unsigned SubRegNo) const {\n" - << " switch (RegNo) {\n" - << " default:\n return 0;\n"; - for (unsigned i = 0, e = Regs.size(); i != e; ++i) { - const CodeGenRegister::SubRegMap &SRM = Regs[i]->getSubRegs(); - if (SRM.empty()) - continue; - OS << " case " << getQualifiedName(Regs[i]->TheDef) << ":\n"; - for (CodeGenRegister::SubRegMap::const_iterator ii = SRM.begin(), - ie = SRM.end(); ii != ie; ++ii) - OS << " if (SubRegNo == " << getQualifiedName(ii->second->TheDef) - << ") return " << getQualifiedName(ii->first) << ";\n"; - OS << " return 0;\n"; - } - OS << " };\n"; - OS << " return 0;\n"; - OS << "}\n\n"; - // Emit composeSubRegIndices OS << "unsigned " << ClassName << "::composeSubRegIndices(unsigned IdxA, unsigned IdxB) const {\n" @@ -763,15 +942,15 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target, for (unsigned i = 0, e = SubRegIndices.size(); i != e; ++i) { bool Open = false; for (unsigned j = 0; j != e; ++j) { - if (Record *Comp = RegBank.getCompositeSubRegIndex(SubRegIndices[i], - SubRegIndices[j])) { + if (CodeGenSubRegIndex *Comp = + SubRegIndices[i]->compose(SubRegIndices[j])) { if (!Open) { - OS << " case " << getQualifiedName(SubRegIndices[i]) + OS << " case " << SubRegIndices[i]->getQualifiedName() << ": switch(IdxB) {\n default: return IdxB;\n"; Open = true; } - OS << " case " << getQualifiedName(SubRegIndices[j]) - << ": return " << getQualifiedName(Comp) << ";\n"; + OS << " case " << SubRegIndices[j]->getQualifiedName() + << ": return " << Comp->getQualifiedName() << ";\n"; } } if (Open) @@ -800,7 +979,7 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target, const CodeGenRegisterClass &RC = *RegisterClasses[rci]; OS << " {\t// " << RC.getName() << "\n"; for (unsigned sri = 0, sre = SubRegIndices.size(); sri != sre; ++sri) { - Record *Idx = SubRegIndices[sri]; + CodeGenSubRegIndex *Idx = SubRegIndices[sri]; if (CodeGenRegisterClass *SRC = RC.getSubClassWithSubReg(Idx)) OS << " " << SRC->EnumValue + 1 << ",\t// " << Idx->getName() << " -> " << SRC->getName() << "\n"; @@ -817,22 +996,106 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target, } OS << "}\n\n"; + // Emit getMatchingSuperRegClass. + OS << "const TargetRegisterClass *" << ClassName + << "::getMatchingSuperRegClass(const TargetRegisterClass *A," + " const TargetRegisterClass *B, unsigned Idx) const {\n"; + if (SubRegIndices.empty()) { + OS << " llvm_unreachable(\"Target has no sub-registers\");\n"; + } else { + // We need to find the largest sub-class of A such that every register has + // an Idx sub-register in B. Map (B, Idx) to a bit-vector of + // super-register classes that map into B. Then compute the largest common + // sub-class with A by taking advantage of the register class ordering, + // like getCommonSubClass(). + + // Bitvector table is NumRCs x NumSubIndexes x BVWords, where BVWords is + // the number of 32-bit words required to represent all register classes. + const unsigned BVWords = (RegisterClasses.size()+31)/32; + BitVector BV(RegisterClasses.size()); + + OS << " static const uint32_t Table[" << RegisterClasses.size() + << "][" << SubRegIndices.size() << "][" << BVWords << "] = {\n"; + for (unsigned rci = 0, rce = RegisterClasses.size(); rci != rce; ++rci) { + const CodeGenRegisterClass &RC = *RegisterClasses[rci]; + OS << " {\t// " << RC.getName() << "\n"; + for (unsigned sri = 0, sre = SubRegIndices.size(); sri != sre; ++sri) { + CodeGenSubRegIndex *Idx = SubRegIndices[sri]; + BV.reset(); + RC.getSuperRegClasses(Idx, BV); + OS << " { "; + printBitVectorAsHex(OS, BV, 32); + OS << "},\t// " << Idx->getName() << '\n'; + } + OS << " },\n"; + } + OS << " };\n assert(A && B && \"Missing regclass\");\n" + << " --Idx;\n" + << " assert(Idx < " << SubRegIndices.size() << " && \"Bad subreg\");\n" + << " const uint32_t *TV = Table[B->getID()][Idx];\n" + << " const uint32_t *SC = A->getSubClassMask();\n" + << " for (unsigned i = 0; i != " << BVWords << "; ++i)\n" + << " if (unsigned Common = TV[i] & SC[i])\n" + << " return getRegClass(32*i + CountTrailingZeros_32(Common));\n" + << " return 0;\n"; + } + OS << "}\n\n"; + + EmitRegUnitPressure(OS, RegBank, ClassName); + // Emit the constructor of the class... - OS << "extern MCRegisterDesc " << TargetName << "RegDesc[];\n"; + OS << "extern const MCRegisterDesc " << TargetName << "RegDesc[];\n"; + OS << "extern const uint16_t " << TargetName << "RegLists[];\n"; + if (SubRegIndices.size() != 0) + OS << "extern const uint16_t *get" << TargetName + << "SubRegTable();\n"; - OS << ClassName << "::" << ClassName + EmitRegMappingTables(OS, Regs, true); + + OS << ClassName << "::\n" << ClassName << "(unsigned RA, unsigned DwarfFlavour, unsigned EHFlavour)\n" << " : TargetRegisterInfo(" << TargetName << "RegInfoDesc" << ", RegisterClasses, RegisterClasses+" << RegisterClasses.size() <<",\n" - << " " << TargetName << "SubRegIndexTable) {\n" + << " " << TargetName << "SubRegIndexTable) {\n" << " InitMCRegisterInfo(" << TargetName << "RegDesc, " - << Regs.size()+1 << ", RA, " << TargetName << "MCRegisterClasses, " - << RegisterClasses.size() << ");\n\n"; + << Regs.size()+1 << ", RA,\n " << TargetName + << "MCRegisterClasses, " << RegisterClasses.size() << ",\n" + << " " << TargetName << "RegLists,\n" + << " "; + if (SubRegIndices.size() != 0) + OS << "get" << TargetName << "SubRegTable(), " + << SubRegIndices.size() << ");\n\n"; + else + OS << "NULL, 0);\n\n"; EmitRegMapping(OS, Regs, true); OS << "}\n\n"; + + // Emit CalleeSavedRegs information. + std::vector<Record*> CSRSets = + Records.getAllDerivedDefinitions("CalleeSavedRegs"); + for (unsigned i = 0, e = CSRSets.size(); i != e; ++i) { + Record *CSRSet = CSRSets[i]; + const SetTheory::RecVec *Regs = RegBank.getSets().expand(CSRSet); + assert(Regs && "Cannot expand CalleeSavedRegs instance"); + + // Emit the *_SaveList list of callee-saved registers. + OS << "static const uint16_t " << CSRSet->getName() + << "_SaveList[] = { "; + for (unsigned r = 0, re = Regs->size(); r != re; ++r) + OS << getQualifiedName((*Regs)[r]) << ", "; + OS << "0 };\n"; + + // Emit the *_RegMask bit mask of call-preserved registers. + OS << "static const uint32_t " << CSRSet->getName() + << "_RegMask[] = { "; + printBitVectorAsHex(OS, RegBank.computeCoveredRegisters(*Regs), 32); + OS << "};\n"; + } + OS << "\n\n"; + OS << "} // End llvm namespace \n"; OS << "#endif // GET_REGINFO_TARGET_DESC\n\n"; } diff --git a/utils/TableGen/RegisterInfoEmitter.h b/utils/TableGen/RegisterInfoEmitter.h index 0fd4d07..ee9903c 100644 --- a/utils/TableGen/RegisterInfoEmitter.h +++ b/utils/TableGen/RegisterInfoEmitter.h @@ -50,7 +50,13 @@ public: private: void EmitRegMapping(raw_ostream &o, const std::vector<CodeGenRegister*> &Regs, bool isCtor); + void EmitRegMappingTables(raw_ostream &o, + const std::vector<CodeGenRegister*> &Regs, + bool isCtor); void EmitRegClasses(raw_ostream &OS, CodeGenTarget &Target); + + void EmitRegUnitPressure(raw_ostream &OS, const CodeGenRegBank &RegBank, + const std::string &ClassName); }; } // End llvm namespace diff --git a/utils/TableGen/SequenceToOffsetTable.h b/utils/TableGen/SequenceToOffsetTable.h new file mode 100644 index 0000000..97c764e --- /dev/null +++ b/utils/TableGen/SequenceToOffsetTable.h @@ -0,0 +1,139 @@ +//===-- SequenceToOffsetTable.h - Compress similar sequences ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// SequenceToOffsetTable can be used to emit a number of null-terminated +// sequences as one big array. Use the same memory when a sequence is a suffix +// of another. +// +//===----------------------------------------------------------------------===// + +#ifndef TBLGEN_SEQUENCE_TO_OFFSET_TABLE_H +#define TBLGEN_SEQUENCE_TO_OFFSET_TABLE_H + +#include "llvm/Support/raw_ostream.h" +#include <functional> +#include <algorithm> +#include <vector> +#include <cassert> +#include <cctype> + +namespace llvm { + +/// SequenceToOffsetTable - Collect a number of terminated sequences of T. +/// Compute the layout of a table that contains all the sequences, possibly by +/// reusing entries. +/// +/// @param SeqT The sequence container. (vector or string). +/// @param Less A stable comparator for SeqT elements. +template<typename SeqT, typename Less = std::less<typename SeqT::value_type> > +class SequenceToOffsetTable { + typedef typename SeqT::value_type ElemT; + + // Define a comparator for SeqT that sorts a suffix immediately before a + // sequence with that suffix. + struct SeqLess : public std::binary_function<SeqT, SeqT, bool> { + Less L; + bool operator()(const SeqT &A, const SeqT &B) const { + return std::lexicographical_compare(A.rbegin(), A.rend(), + B.rbegin(), B.rend(), L); + } + }; + + // Keep sequences ordered according to SeqLess so suffixes are easy to find. + // Map each sequence to its offset in the table. + typedef std::map<SeqT, unsigned, SeqLess> SeqMap; + + // Sequences added so far, with suffixes removed. + SeqMap Seqs; + + // Entries in the final table, or 0 before layout was called. + unsigned Entries; + + // isSuffix - Returns true if A is a suffix of B. + static bool isSuffix(const SeqT &A, const SeqT &B) { + return A.size() <= B.size() && std::equal(A.rbegin(), A.rend(), B.rbegin()); + } + +public: + SequenceToOffsetTable() : Entries(0) {} + + /// add - Add a sequence to the table. + /// This must be called before layout(). + void add(const SeqT &Seq) { + assert(Entries == 0 && "Cannot call add() after layout()"); + typename SeqMap::iterator I = Seqs.lower_bound(Seq); + + // If SeqMap contains a sequence that has Seq as a suffix, I will be + // pointing to it. + if (I != Seqs.end() && isSuffix(Seq, I->first)) + return; + + I = Seqs.insert(I, std::make_pair(Seq, 0u)); + + // The entry before I may be a suffix of Seq that can now be erased. + if (I != Seqs.begin() && isSuffix((--I)->first, Seq)) + Seqs.erase(I); + } + + /// layout - Computes the final table layout. + void layout() { + assert(Entries == 0 && "Can only call layout() once"); + // Lay out the table in Seqs iteration order. + for (typename SeqMap::iterator I = Seqs.begin(), E = Seqs.end(); I != E; + ++I) { + I->second = Entries; + // Include space for a terminator. + Entries += I->first.size() + 1; + } + } + + /// get - Returns the offset of Seq in the final table. + unsigned get(const SeqT &Seq) const { + assert(Entries && "Call layout() before get()"); + typename SeqMap::const_iterator I = Seqs.lower_bound(Seq); + assert(I != Seqs.end() && isSuffix(Seq, I->first) && + "get() called with sequence that wasn't added first"); + return I->second + (I->first.size() - Seq.size()); + } + + /// emit - Print out the table as the body of an array initializer. + /// Use the Print function to print elements. + void emit(raw_ostream &OS, + void (*Print)(raw_ostream&, ElemT), + const char *Term = "0") const { + assert(Entries && "Call layout() before emit()"); + for (typename SeqMap::const_iterator I = Seqs.begin(), E = Seqs.end(); + I != E; ++I) { + OS << " /* " << I->second << " */ "; + for (typename SeqT::const_iterator SI = I->first.begin(), + SE = I->first.end(); SI != SE; ++SI) { + Print(OS, *SI); + OS << ", "; + } + OS << Term << ",\n"; + } + } +}; + +// Helper function for SequenceToOffsetTable<string>. +static inline void printChar(raw_ostream &OS, char C) { + unsigned char UC(C); + if (isalnum(UC) || ispunct(UC)) { + OS << '\''; + if (C == '\\' || C == '\'') + OS << '\\'; + OS << C << '\''; + } else { + OS << unsigned(UC); + } +} + +} // end namespace llvm + +#endif diff --git a/utils/TableGen/SetTheory.cpp b/utils/TableGen/SetTheory.cpp index bef73f3..0649fd1 100644 --- a/utils/TableGen/SetTheory.cpp +++ b/utils/TableGen/SetTheory.cpp @@ -139,6 +139,24 @@ struct DecimateOp : public SetIntBinOp { } }; +// (interleave S1, S2, ...) Interleave elements of the arguments. +struct InterleaveOp : public SetTheory::Operator { + void apply(SetTheory &ST, DagInit *Expr, RecSet &Elts) { + // Evaluate the arguments individually. + SmallVector<RecSet, 4> Args(Expr->getNumArgs()); + unsigned MaxSize = 0; + for (unsigned i = 0, e = Expr->getNumArgs(); i != e; ++i) { + ST.evaluate(Expr->getArg(i), Args[i]); + MaxSize = std::max(MaxSize, unsigned(Args[i].size())); + } + // Interleave arguments into Elts. + for (unsigned n = 0; n != MaxSize; ++n) + for (unsigned i = 0, e = Expr->getNumArgs(); i != e; ++i) + if (n < Args[i].size()) + Elts.insert(Args[i][n]); + } +}; + // (sequence "Format", From, To) Generate a sequence of records by name. struct SequenceOp : public SetTheory::Operator { void apply(SetTheory &ST, DagInit *Expr, RecSet &Elts) { @@ -198,6 +216,10 @@ struct FieldExpander : public SetTheory::Expander { }; } // end anonymous namespace +void SetTheory::Operator::anchor() { } + +void SetTheory::Expander::anchor() { } + SetTheory::SetTheory() { addOperator("add", new AddOp); addOperator("sub", new SubOp); @@ -207,6 +229,7 @@ SetTheory::SetTheory() { addOperator("rotl", new RotOp(false)); addOperator("rotr", new RotOp(true)); addOperator("decimate", new DecimateOp); + addOperator("interleave", new InterleaveOp); addOperator("sequence", new SequenceOp); } diff --git a/utils/TableGen/SetTheory.h b/utils/TableGen/SetTheory.h index 6e8313b..b394058 100644 --- a/utils/TableGen/SetTheory.h +++ b/utils/TableGen/SetTheory.h @@ -65,7 +65,9 @@ public: typedef SmallSetVector<Record*, 16> RecSet; /// Operator - A callback representing a DAG operator. - struct Operator { + class Operator { + virtual void anchor(); + public: virtual ~Operator() {} /// apply - Apply this operator to Expr's arguments and insert the result @@ -76,7 +78,9 @@ public: /// Expander - A callback function that can transform a Record representing a /// set into a fully expanded list of elements. Expanders provide a way for /// users to define named sets that can be used in DAG expressions. - struct Expander { + class Expander { + virtual void anchor(); + public: virtual ~Expander() {} virtual void expand(SetTheory&, Record*, RecSet &Elts) =0; diff --git a/utils/TableGen/StringToOffsetTable.h b/utils/TableGen/StringToOffsetTable.h index ac9422c..803f5bd 100644 --- a/utils/TableGen/StringToOffsetTable.h +++ b/utils/TableGen/StringToOffsetTable.h @@ -26,16 +26,17 @@ class StringToOffsetTable { std::string AggregateString; public: - unsigned GetOrAddStringOffset(StringRef Str) { - unsigned &Entry = StringOffset[Str]; - if (Entry == 0) { + unsigned GetOrAddStringOffset(StringRef Str, bool appendZero = true) { + StringMapEntry<unsigned> &Entry = StringOffset.GetOrCreateValue(Str, -1U); + if (Entry.getValue() == -1U) { // Add the string to the aggregate if this is the first time found. - Entry = AggregateString.size(); + Entry.setValue(AggregateString.size()); AggregateString.append(Str.begin(), Str.end()); - AggregateString += '\0'; + if (appendZero) + AggregateString += '\0'; } - return Entry; + return Entry.getValue(); } void EmitString(raw_ostream &O) { diff --git a/utils/TableGen/SubtargetEmitter.cpp b/utils/TableGen/SubtargetEmitter.cpp index 103a403..986c50f 100644 --- a/utils/TableGen/SubtargetEmitter.cpp +++ b/utils/TableGen/SubtargetEmitter.cpp @@ -39,28 +39,41 @@ void SubtargetEmitter::Enumeration(raw_ostream &OS, OS << "namespace " << Target << " {\n"; - // Open enumeration - OS << "enum {\n"; + // For bit flag enumerations with more than 32 items, emit constants. + // Emit an enum for everything else. + if (isBits && N > 32) { + // For each record + for (unsigned i = 0; i < N; i++) { + // Next record + Record *Def = DefList[i]; + + // Get and emit name and expression (1 << i) + OS << " const uint64_t " << Def->getName() << " = 1ULL << " << i << ";\n"; + } + } else { + // Open enumeration + OS << "enum {\n"; - // For each record - for (unsigned i = 0; i < N;) { - // Next record - Record *Def = DefList[i]; + // For each record + for (unsigned i = 0; i < N;) { + // Next record + Record *Def = DefList[i]; - // Get and emit name - OS << " " << Def->getName(); + // Get and emit name + OS << " " << Def->getName(); - // If bit flags then emit expression (1 << i) - if (isBits) OS << " = " << " 1ULL << " << i; + // If bit flags then emit expression (1 << i) + if (isBits) OS << " = " << " 1ULL << " << i; - // Depending on 'if more in the list' emit comma - if (++i < N) OS << ","; + // Depending on 'if more in the list' emit comma + if (++i < N) OS << ","; - OS << "\n"; - } + OS << "\n"; + } - // Close enumeration - OS << "};\n"; + // Close enumeration + OS << "};\n"; + } OS << "}\n"; } @@ -81,7 +94,8 @@ unsigned SubtargetEmitter::FeatureKeyValues(raw_ostream &OS) { // Begin feature table OS << "// Sorted (by key) array of values for CPU features.\n" - << "llvm::SubtargetFeatureKV " << Target << "FeatureKV[] = {\n"; + << "extern const llvm::SubtargetFeatureKV " << Target + << "FeatureKV[] = {\n"; // For each feature unsigned NumFeatures = 0; @@ -140,7 +154,8 @@ unsigned SubtargetEmitter::CPUKeyValues(raw_ostream &OS) { // Begin processor table OS << "// Sorted (by key) array of values for CPU subtype.\n" - << "llvm::SubtargetFeatureKV " << Target << "SubTypeKV[] = {\n"; + << "extern const llvm::SubtargetFeatureKV " << Target + << "SubTypeKV[] = {\n"; // For each processor for (unsigned i = 0, N = ProcessorList.size(); i < N;) { @@ -327,9 +342,9 @@ void SubtargetEmitter::EmitStageAndOperandCycleData(raw_ostream &OS, OS << "\n// Pipeline forwarding pathes for itineraries \"" << Name << "\"\n" << "namespace " << Name << "Bypass {\n"; - OS << " unsigned NoBypass = 0;\n"; + OS << " const unsigned NoBypass = 0;\n"; for (unsigned j = 0, BPN = BPs.size(); j < BPN; ++j) - OS << " unsigned " << BPs[j]->getName() + OS << " const unsigned " << BPs[j]->getName() << " = 1 << " << j << ";\n"; OS << "}\n"; @@ -337,16 +352,17 @@ void SubtargetEmitter::EmitStageAndOperandCycleData(raw_ostream &OS, } // Begin stages table - std::string StageTable = "\nllvm::InstrStage " + Target + "Stages[] = {\n"; + std::string StageTable = "\nextern const llvm::InstrStage " + Target + + "Stages[] = {\n"; StageTable += " { 0, 0, 0, llvm::InstrStage::Required }, // No itinerary\n"; // Begin operand cycle table - std::string OperandCycleTable = "unsigned " + Target + + std::string OperandCycleTable = "extern const unsigned " + Target + "OperandCycles[] = {\n"; OperandCycleTable += " 0, // No itinerary\n"; // Begin pipeline bypass table - std::string BypassTable = "unsigned " + Target + + std::string BypassTable = "extern const unsigned " + Target + "ForwardingPathes[] = {\n"; BypassTable += " 0, // No itinerary\n"; @@ -488,7 +504,7 @@ EmitProcessorData(raw_ostream &OS, // Begin processor itinerary table OS << "\n"; - OS << "llvm::InstrItinerary " << Name << "[] = {\n"; + OS << "static const llvm::InstrItinerary " << Name << "[] = {\n"; // For each itinerary class std::vector<InstrItinerary> &ItinList = *ProcListIter++; @@ -530,7 +546,7 @@ void SubtargetEmitter::EmitProcessorLookup(raw_ostream &OS) { // Begin processor table OS << "\n"; OS << "// Sorted (by key) array of itineraries for CPU subtype.\n" - << "llvm::SubtargetInfoKV " + << "extern const llvm::SubtargetInfoKV " << Target << "ProcItinKV[] = {\n"; // For each processor @@ -708,9 +724,13 @@ void SubtargetEmitter::run(raw_ostream &OS) { std::string ClassName = Target + "GenSubtargetInfo"; OS << "namespace llvm {\n"; + OS << "class DFAPacketizer;\n"; OS << "struct " << ClassName << " : public TargetSubtargetInfo {\n" << " explicit " << ClassName << "(StringRef TT, StringRef CPU, " << "StringRef FS);\n" + << "public:\n" + << " DFAPacketizer *createDFAPacketizer(const InstrItineraryData *IID)" + << " const;\n" << "};\n"; OS << "} // End llvm namespace \n"; @@ -720,13 +740,13 @@ void SubtargetEmitter::run(raw_ostream &OS) { OS << "#undef GET_SUBTARGETINFO_CTOR\n"; OS << "namespace llvm {\n"; - OS << "extern llvm::SubtargetFeatureKV " << Target << "FeatureKV[];\n"; - OS << "extern llvm::SubtargetFeatureKV " << Target << "SubTypeKV[];\n"; + OS << "extern const llvm::SubtargetFeatureKV " << Target << "FeatureKV[];\n"; + OS << "extern const llvm::SubtargetFeatureKV " << Target << "SubTypeKV[];\n"; if (HasItineraries) { - OS << "extern llvm::SubtargetInfoKV " << Target << "ProcItinKV[];\n"; - OS << "extern llvm::InstrStage " << Target << "Stages[];\n"; - OS << "extern unsigned " << Target << "OperandCycles[];\n"; - OS << "extern unsigned " << Target << "ForwardingPathes[];\n"; + OS << "extern const llvm::SubtargetInfoKV " << Target << "ProcItinKV[];\n"; + OS << "extern const llvm::InstrStage " << Target << "Stages[];\n"; + OS << "extern const unsigned " << Target << "OperandCycles[];\n"; + OS << "extern const unsigned " << Target << "ForwardingPathes[];\n"; } OS << ClassName << "::" << ClassName << "(StringRef TT, StringRef CPU, " diff --git a/utils/TableGen/TableGen.cpp b/utils/TableGen/TableGen.cpp index eacfdf6..8c41358 100644 --- a/utils/TableGen/TableGen.cpp +++ b/utils/TableGen/TableGen.cpp @@ -16,6 +16,7 @@ #include "CallingConvEmitter.h" #include "CodeEmitterGen.h" #include "DAGISelEmitter.h" +#include "DFAPacketizerEmitter.h" #include "DisassemblerEmitter.h" #include "EDEmitter.h" #include "FastISelEmitter.h" @@ -23,7 +24,6 @@ #include "IntrinsicEmitter.h" #include "PseudoLoweringEmitter.h" #include "RegisterInfoEmitter.h" -#include "ARMDecoderEmitter.h" #include "SubtargetEmitter.h" #include "SetTheory.h" @@ -44,11 +44,11 @@ enum ActionType { GenInstrInfo, GenAsmWriter, GenAsmMatcher, - GenARMDecoder, GenDisassembler, GenPseudoLowering, GenCallingConv, GenDAGISel, + GenDFAPacketizer, GenFastISel, GenSubtarget, GenIntrinsic, @@ -73,8 +73,6 @@ namespace { "Generate calling convention descriptions"), clEnumValN(GenAsmWriter, "gen-asm-writer", "Generate assembly writer"), - clEnumValN(GenARMDecoder, "gen-arm-decoder", - "Generate decoders for ARM/Thumb"), clEnumValN(GenDisassembler, "gen-disassembler", "Generate disassembler"), clEnumValN(GenPseudoLowering, "gen-pseudo-lowering", @@ -83,6 +81,8 @@ namespace { "Generate assembly instruction matcher"), clEnumValN(GenDAGISel, "gen-dag-isel", "Generate a DAG instruction selector"), + clEnumValN(GenDFAPacketizer, "gen-dfa-packetizer", + "Generate DFA Packetizer for VLIW targets"), clEnumValN(GenFastISel, "gen-fast-isel", "Generate a \"fast\" instruction selector"), clEnumValN(GenSubtarget, "gen-subtarget", @@ -101,92 +101,89 @@ namespace { cl::opt<std::string> Class("class", cl::desc("Print Enum list for this class"), - cl::value_desc("class name")); -} - -class LLVMTableGenAction : public TableGenAction { -public: - bool operator()(raw_ostream &OS, RecordKeeper &Records) { - switch (Action) { - case PrintRecords: - OS << Records; // No argument, dump all contents - break; - case GenEmitter: - CodeEmitterGen(Records).run(OS); - break; - case GenRegisterInfo: - RegisterInfoEmitter(Records).run(OS); - break; - case GenInstrInfo: - InstrInfoEmitter(Records).run(OS); - break; - case GenCallingConv: - CallingConvEmitter(Records).run(OS); - break; - case GenAsmWriter: - AsmWriterEmitter(Records).run(OS); - break; - case GenARMDecoder: - ARMDecoderEmitter(Records).run(OS); - break; - case GenAsmMatcher: - AsmMatcherEmitter(Records).run(OS); - break; - case GenDisassembler: - DisassemblerEmitter(Records).run(OS); - break; - case GenPseudoLowering: - PseudoLoweringEmitter(Records).run(OS); - break; - case GenDAGISel: - DAGISelEmitter(Records).run(OS); - break; - case GenFastISel: - FastISelEmitter(Records).run(OS); - break; - case GenSubtarget: - SubtargetEmitter(Records).run(OS); - break; - case GenIntrinsic: - IntrinsicEmitter(Records).run(OS); - break; - case GenTgtIntrinsic: - IntrinsicEmitter(Records, true).run(OS); - break; - case GenEDInfo: - EDEmitter(Records).run(OS); - break; - case PrintEnums: - { - std::vector<Record*> Recs = Records.getAllDerivedDefinitions(Class); - for (unsigned i = 0, e = Recs.size(); i != e; ++i) - OS << Recs[i]->getName() << ", "; - OS << "\n"; - break; - } - case PrintSets: - { - SetTheory Sets; - Sets.addFieldExpander("Set", "Elements"); - std::vector<Record*> Recs = Records.getAllDerivedDefinitions("Set"); - for (unsigned i = 0, e = Recs.size(); i != e; ++i) { - OS << Recs[i]->getName() << " = ["; - const std::vector<Record*> *Elts = Sets.expand(Recs[i]); - assert(Elts && "Couldn't expand Set instance"); - for (unsigned ei = 0, ee = Elts->size(); ei != ee; ++ei) - OS << ' ' << (*Elts)[ei]->getName(); - OS << " ]\n"; + cl::value_desc("class name")); + + class LLVMTableGenAction : public TableGenAction { + public: + bool operator()(raw_ostream &OS, RecordKeeper &Records) { + switch (Action) { + case PrintRecords: + OS << Records; // No argument, dump all contents + break; + case GenEmitter: + CodeEmitterGen(Records).run(OS); + break; + case GenRegisterInfo: + RegisterInfoEmitter(Records).run(OS); + break; + case GenInstrInfo: + InstrInfoEmitter(Records).run(OS); + break; + case GenCallingConv: + CallingConvEmitter(Records).run(OS); + break; + case GenAsmWriter: + AsmWriterEmitter(Records).run(OS); + break; + case GenAsmMatcher: + AsmMatcherEmitter(Records).run(OS); + break; + case GenDisassembler: + DisassemblerEmitter(Records).run(OS); + break; + case GenPseudoLowering: + PseudoLoweringEmitter(Records).run(OS); + break; + case GenDAGISel: + DAGISelEmitter(Records).run(OS); + break; + case GenDFAPacketizer: + DFAGen(Records).run(OS); + break; + case GenFastISel: + FastISelEmitter(Records).run(OS); + break; + case GenSubtarget: + SubtargetEmitter(Records).run(OS); + break; + case GenIntrinsic: + IntrinsicEmitter(Records).run(OS); + break; + case GenTgtIntrinsic: + IntrinsicEmitter(Records, true).run(OS); + break; + case GenEDInfo: + EDEmitter(Records).run(OS); + break; + case PrintEnums: + { + std::vector<Record*> Recs = Records.getAllDerivedDefinitions(Class); + for (unsigned i = 0, e = Recs.size(); i != e; ++i) + OS << Recs[i]->getName() << ", "; + OS << "\n"; + break; } - break; - } - default: - assert(1 && "Invalid Action"); - return true; + case PrintSets: + { + SetTheory Sets; + Sets.addFieldExpander("Set", "Elements"); + std::vector<Record*> Recs = Records.getAllDerivedDefinitions("Set"); + for (unsigned i = 0, e = Recs.size(); i != e; ++i) { + OS << Recs[i]->getName() << " = ["; + const std::vector<Record*> *Elts = Sets.expand(Recs[i]); + assert(Elts && "Couldn't expand Set instance"); + for (unsigned ei = 0, ee = Elts->size(); ei != ee; ++ei) + OS << ' ' << (*Elts)[ei]->getName(); + OS << " ]\n"; + } + break; + } + } + + return false; } - - return false; - } -}; + }; +} int main(int argc, char **argv) { sys::PrintStackTraceOnErrorSignal(); diff --git a/utils/TableGen/X86DisassemblerTables.cpp b/utils/TableGen/X86DisassemblerTables.cpp index e8c9a48..2875168 100644 --- a/utils/TableGen/X86DisassemblerTables.cpp +++ b/utils/TableGen/X86DisassemblerTables.cpp @@ -41,15 +41,20 @@ static inline bool inheritsFrom(InstructionContext child, case IC: return(inheritsFrom(child, IC_64BIT) || inheritsFrom(child, IC_OPSIZE) || + inheritsFrom(child, IC_ADSIZE) || inheritsFrom(child, IC_XD) || inheritsFrom(child, IC_XS)); case IC_64BIT: return(inheritsFrom(child, IC_64BIT_REXW) || inheritsFrom(child, IC_64BIT_OPSIZE) || + inheritsFrom(child, IC_64BIT_ADSIZE) || inheritsFrom(child, IC_64BIT_XD) || inheritsFrom(child, IC_64BIT_XS)); case IC_OPSIZE: return inheritsFrom(child, IC_64BIT_OPSIZE); + case IC_ADSIZE: + case IC_64BIT_ADSIZE: + return false; case IC_XD: return inheritsFrom(child, IC_64BIT_XD); case IC_XS: @@ -95,11 +100,13 @@ static inline bool inheritsFrom(InstructionContext child, case IC_VEX_L: case IC_VEX_L_XS: case IC_VEX_L_XD: + return false; case IC_VEX_L_OPSIZE: + return inheritsFrom(child, IC_VEX_L_W_OPSIZE); + case IC_VEX_L_W_OPSIZE: return false; default: llvm_unreachable("Unknown instruction class"); - return false; } } @@ -138,8 +145,6 @@ static inline const char* stringForContext(InstructionContext insnContext) { INSTRUCTION_CONTEXTS #undef ENUM_ENTRY } - - return 0; } /// stringForOperandType - Like stringForContext, but for OperandTypes. @@ -194,8 +199,7 @@ void DisassemblerTables::emitOneID(raw_ostream &o, /// @param i - The indentation level for that output stream. static void emitEmptyTable(raw_ostream &o, uint32_t &i) { - o.indent(i * 2) << "static const InstrUID modRMEmptyTable[1] = { 0 };\n"; - o << "\n"; + o.indent(i * 2) << "0x0, /* EmptyTable */\n"; } /// getDecisionType - Determines whether a ModRM decision with 255 entries can @@ -207,28 +211,40 @@ static ModRMDecisionType getDecisionType(ModRMDecision &decision) { bool satisfiesOneEntry = true; bool satisfiesSplitRM = true; - + bool satisfiesSplitReg = true; + uint16_t index; - + for (index = 0; index < 256; ++index) { if (decision.instructionIDs[index] != decision.instructionIDs[0]) satisfiesOneEntry = false; - + if (((index & 0xc0) == 0xc0) && (decision.instructionIDs[index] != decision.instructionIDs[0xc0])) satisfiesSplitRM = false; - + if (((index & 0xc0) != 0xc0) && (decision.instructionIDs[index] != decision.instructionIDs[0x00])) satisfiesSplitRM = false; + + if (((index & 0xc0) == 0xc0) && + (decision.instructionIDs[index] != decision.instructionIDs[index&0xf8])) + satisfiesSplitReg = false; + + if (((index & 0xc0) != 0xc0) && + (decision.instructionIDs[index] != decision.instructionIDs[index&0x38])) + satisfiesSplitReg = false; } - + if (satisfiesOneEntry) return MODRM_ONEENTRY; - + if (satisfiesSplitRM) return MODRM_SPLITRM; - + + if (satisfiesSplitReg) + return MODRM_SPLITREG; + return MODRM_FULL; } @@ -291,71 +307,76 @@ void DisassemblerTables::emitModRMDecision(raw_ostream &o1, ModRMDecision &decision) const { static uint64_t sTableNumber = 0; - uint64_t thisTableNumber = sTableNumber; + static uint64_t sEntryNumber = 1; ModRMDecisionType dt = getDecisionType(decision); uint16_t index; - + if (dt == MODRM_ONEENTRY && decision.instructionIDs[0] == 0) { o2.indent(i2) << "{ /* ModRMDecision */" << "\n"; i2++; - + o2.indent(i2) << stringForDecisionType(dt) << "," << "\n"; - o2.indent(i2) << "modRMEmptyTable"; - + o2.indent(i2) << 0 << " /* EmptyTable */\n"; + i2--; o2.indent(i2) << "}"; return; } - - o1.indent(i1) << "static const InstrUID modRMTable" << thisTableNumber; - - switch (dt) { - default: - llvm_unreachable("Unknown decision type"); - case MODRM_ONEENTRY: - o1 << "[1]"; - break; - case MODRM_SPLITRM: - o1 << "[2]"; - break; - case MODRM_FULL: - o1 << "[256]"; - break; - } - o1 << " = {" << "\n"; + o1 << "/* Table" << sTableNumber << " */\n"; i1++; - + switch (dt) { default: llvm_unreachable("Unknown decision type"); case MODRM_ONEENTRY: - emitOneID(o1, i1, decision.instructionIDs[0], false); + emitOneID(o1, i1, decision.instructionIDs[0], true); break; case MODRM_SPLITRM: emitOneID(o1, i1, decision.instructionIDs[0x00], true); // mod = 0b00 - emitOneID(o1, i1, decision.instructionIDs[0xc0], false); // mod = 0b11 + emitOneID(o1, i1, decision.instructionIDs[0xc0], true); // mod = 0b11 + break; + case MODRM_SPLITREG: + for (index = 0; index < 64; index += 8) + emitOneID(o1, i1, decision.instructionIDs[index], true); + for (index = 0xc0; index < 256; index += 8) + emitOneID(o1, i1, decision.instructionIDs[index], true); break; case MODRM_FULL: for (index = 0; index < 256; ++index) - emitOneID(o1, i1, decision.instructionIDs[index], index < 255); + emitOneID(o1, i1, decision.instructionIDs[index], true); break; } - + i1--; - o1.indent(i1) << "};" << "\n"; - o1 << "\n"; - + o2.indent(i2) << "{ /* struct ModRMDecision */" << "\n"; i2++; - + o2.indent(i2) << stringForDecisionType(dt) << "," << "\n"; - o2.indent(i2) << "modRMTable" << sTableNumber << "\n"; - + o2.indent(i2) << sEntryNumber << " /* Table" << sTableNumber << " */\n"; + i2--; o2.indent(i2) << "}"; - + + switch (dt) { + default: + llvm_unreachable("Unknown decision type"); + case MODRM_ONEENTRY: + sEntryNumber += 1; + break; + case MODRM_SPLITRM: + sEntryNumber += 2; + break; + case MODRM_SPLITREG: + sEntryNumber += 16; + break; + case MODRM_FULL: + sEntryNumber += 256; + break; + } + ++sTableNumber; } @@ -436,11 +457,11 @@ void DisassemblerTables::emitInstructionInfo(raw_ostream &o, uint32_t &i) for (index = 0; index < numInstructions; ++index) { o.indent(i * 2) << "{ /* " << index << " */" << "\n"; i++; - - o.indent(i * 2) << - stringForModifierType(InstructionSpecifiers[index].modifierType); + + o.indent(i * 2) << stringForModifierType( + (ModifierType)InstructionSpecifiers[index].modifierType); o << "," << "\n"; - + o.indent(i * 2) << "0x"; o << format("%02hhx", (uint16_t)InstructionSpecifiers[index].modifierBase); o << "," << "\n"; @@ -450,11 +471,11 @@ void DisassemblerTables::emitInstructionInfo(raw_ostream &o, uint32_t &i) for (operandIndex = 0; operandIndex < X86_MAX_OPERANDS; ++operandIndex) { o.indent(i * 2) << "{ "; - o << stringForOperandEncoding(InstructionSpecifiers[index] - .operands[operandIndex] - .encoding); + o <<stringForOperandEncoding((OperandEncoding)InstructionSpecifiers[index] + .operands[operandIndex] + .encoding); o << ", "; - o << stringForOperandType(InstructionSpecifiers[index] + o << stringForOperandType((OperandType)InstructionSpecifiers[index] .operands[operandIndex] .type); o << " }"; @@ -468,7 +489,7 @@ void DisassemblerTables::emitInstructionInfo(raw_ostream &o, uint32_t &i) i--; o.indent(i * 2) << "}," << "\n"; - o.indent(i * 2) << "\"" << InstructionSpecifiers[index].name << "\""; + o.indent(i * 2) << "/* " << InstructionSpecifiers[index].name << " */"; o << "\n"; i--; @@ -494,7 +515,9 @@ void DisassemblerTables::emitContextTable(raw_ostream &o, uint32_t &i) const { for (index = 0; index < 256; ++index) { o.indent(i * 2); - if ((index & ATTR_VEXL) && (index & ATTR_OPSIZE)) + if ((index & ATTR_VEXL) && (index & ATTR_REXW) && (index & ATTR_OPSIZE)) + o << "IC_VEX_L_W_OPSIZE"; + else if ((index & ATTR_VEXL) && (index & ATTR_OPSIZE)) o << "IC_VEX_L_OPSIZE"; else if ((index & ATTR_VEXL) && (index & ATTR_XD)) o << "IC_VEX_L_XD"; @@ -535,6 +558,8 @@ void DisassemblerTables::emitContextTable(raw_ostream &o, uint32_t &i) const { o << "IC_64BIT_XD"; else if ((index & ATTR_64BIT) && (index & ATTR_OPSIZE)) o << "IC_64BIT_OPSIZE"; + else if ((index & ATTR_64BIT) && (index & ATTR_ADSIZE)) + o << "IC_64BIT_ADSIZE"; else if ((index & ATTR_64BIT) && (index & ATTR_REXW)) o << "IC_64BIT_REXW"; else if ((index & ATTR_64BIT)) @@ -549,6 +574,8 @@ void DisassemblerTables::emitContextTable(raw_ostream &o, uint32_t &i) const { o << "IC_XD"; else if (index & ATTR_OPSIZE) o << "IC_OPSIZE"; + else if (index & ATTR_ADSIZE) + o << "IC_ADSIZE"; else o << "IC"; @@ -594,11 +621,16 @@ void DisassemblerTables::emit(raw_ostream &o) const { emitContextTable(o, i2); o << "\n"; - + + o << "static const InstrUID modRMTable[] = {\n"; + i1++; emitEmptyTable(o1, i1); + i1--; emitContextDecisions(o1, o2, i1, i2); - + o << o1.str(); + o << " 0x0\n"; + o << "};\n"; o << "\n"; o << o2.str(); o << "\n"; diff --git a/utils/TableGen/X86ModRMFilters.cpp b/utils/TableGen/X86ModRMFilters.cpp new file mode 100644 index 0000000..7166fe0 --- /dev/null +++ b/utils/TableGen/X86ModRMFilters.cpp @@ -0,0 +1,26 @@ +//===- X86ModRMFilters.cpp - Disassembler ModR/M filterss -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "X86ModRMFilters.h" + +using namespace llvm::X86Disassembler; + +void ModRMFilter::anchor() { } + +void DumbFilter::anchor() { } + +void ModFilter::anchor() { } + +void EscapeFilter::anchor() { } + +void AddRegEscapeFilter::anchor() { } + +void ExtendedFilter::anchor() { } + +void ExactFilter::anchor() { } diff --git a/utils/TableGen/X86ModRMFilters.h b/utils/TableGen/X86ModRMFilters.h index 199040b..19fecbc 100644 --- a/utils/TableGen/X86ModRMFilters.h +++ b/utils/TableGen/X86ModRMFilters.h @@ -27,6 +27,7 @@ namespace X86Disassembler { /// ModRMFilter - Abstract base class for clases that recognize patterns in /// ModR/M bytes. class ModRMFilter { + virtual void anchor(); public: /// Destructor - Override as necessary. virtual ~ModRMFilter() { } @@ -49,6 +50,7 @@ public: /// require a ModR/M byte or instructions where the entire ModR/M byte is used /// for operands. class DumbFilter : public ModRMFilter { + virtual void anchor(); public: bool isDumb() const { return true; @@ -63,7 +65,7 @@ public: /// Some instructions are classified based on whether they are 11 or anything /// else. This filter performs that classification. class ModFilter : public ModRMFilter { -private: + virtual void anchor(); bool R; public: /// Constructor @@ -90,7 +92,7 @@ public: /// possible value. Otherwise, there is one instruction for each value of the /// nnn field [bits 5-3], known elsewhere as the reg field. class EscapeFilter : public ModRMFilter { -private: + virtual void anchor(); bool C0_FF; uint8_t NNN_or_ModRM; public: @@ -121,7 +123,7 @@ public: /// maps to a single instruction. Such instructions require the ModR/M byte /// to fall between 0xc0 and 0xff. class AddRegEscapeFilter : public ModRMFilter { -private: + virtual void anchor(); uint8_t ModRM; public: /// Constructor @@ -142,7 +144,7 @@ public: /// ExtendedFilter - Extended opcodes are classified based on the value of the /// mod field [bits 7-6] and the value of the nnn field [bits 5-3]. class ExtendedFilter : public ModRMFilter { -private: + virtual void anchor(); bool R; uint8_t NNN; public: @@ -169,9 +171,8 @@ public: /// ExactFilter - The occasional extended opcode (such as VMCALL or MONITOR) /// requires the ModR/M byte to have a specific value. -class ExactFilter : public ModRMFilter -{ -private: +class ExactFilter : public ModRMFilter { + virtual void anchor(); uint8_t ModRM; public: /// Constructor diff --git a/utils/TableGen/X86RecognizableInstr.cpp b/utils/TableGen/X86RecognizableInstr.cpp index cae8237..6a01cce 100644 --- a/utils/TableGen/X86RecognizableInstr.cpp +++ b/utils/TableGen/X86RecognizableInstr.cpp @@ -36,7 +36,16 @@ using namespace llvm; MAP(F8, 41) \ MAP(F9, 42) \ MAP(D0, 45) \ - MAP(D1, 46) + MAP(D1, 46) \ + MAP(D4, 47) \ + MAP(D8, 48) \ + MAP(D9, 49) \ + MAP(DA, 50) \ + MAP(DB, 51) \ + MAP(DC, 52) \ + MAP(DD, 53) \ + MAP(DE, 54) \ + MAP(DF, 55) // A clone of X86 since we can't depend on something that is generated. namespace X86Local { @@ -68,7 +77,7 @@ namespace X86Local { DC = 7, DD = 8, DE = 9, DF = 10, XD = 11, XS = 12, T8 = 13, P_TA = 14, - A6 = 15, A7 = 16, TF = 17 + A6 = 15, A7 = 16, T8XD = 17, T8XS = 18, TAXD = 19 }; } @@ -119,6 +128,9 @@ namespace X86Local { EXTENSION_TABLE(ba) \ EXTENSION_TABLE(c7) +#define THREE_BYTE_38_EXTENSION_TABLES \ + EXTENSION_TABLE(F3) + using namespace X86Disassembler; /// needsModRMForDecode - Indicates whether a particular instruction requires a @@ -213,10 +225,13 @@ RecognizableInstr::RecognizableInstr(DisassemblerTables &tables, SegOvr = byteFromRec(Rec, "SegOvrBits"); HasOpSizePrefix = Rec->getValueAsBit("hasOpSizePrefix"); + HasAdSizePrefix = Rec->getValueAsBit("hasAdSizePrefix"); HasREX_WPrefix = Rec->getValueAsBit("hasREX_WPrefix"); HasVEXPrefix = Rec->getValueAsBit("hasVEXPrefix"); HasVEX_4VPrefix = Rec->getValueAsBit("hasVEX_4VPrefix"); + HasVEX_4VOp3Prefix = Rec->getValueAsBit("hasVEX_4VOp3Prefix"); HasVEX_WPrefix = Rec->getValueAsBit("hasVEX_WPrefix"); + HasMemOp4Prefix = Rec->getValueAsBit("hasMemOp4Prefix"); IgnoresVEX_L = Rec->getValueAsBit("ignoresVEX_L"); HasLockPrefix = Rec->getValueAsBit("hasLockPrefix"); IsCodeGenOnly = Rec->getValueAsBit("isCodeGenOnly"); @@ -230,7 +245,7 @@ RecognizableInstr::RecognizableInstr(DisassemblerTables &tables, (Name.find("CRC32") != Name.npos); HasFROperands = hasFROperands(); HasVEX_LPrefix = has256BitOperands() || Rec->getValueAsBit("hasVEX_L"); - + // Check for 64-bit inst which does not require REX Is32Bit = false; Is64Bit = false; @@ -254,10 +269,6 @@ RecognizableInstr::RecognizableInstr(DisassemblerTables &tables, Rec->getName() == "PUSHFS64" || Rec->getName() == "PUSHGS64" || Rec->getName() == "REX64_PREFIX" || - Rec->getName().find("VMREAD64") != Name.npos || - Rec->getName().find("VMWRITE64") != Name.npos || - Rec->getName().find("INVEPT64") != Name.npos || - Rec->getName().find("INVVPID64") != Name.npos || Rec->getName().find("MOV64") != Name.npos || Rec->getName().find("PUSH64") != Name.npos || Rec->getName().find("POP64") != Name.npos; @@ -284,67 +295,90 @@ void RecognizableInstr::processInstr(DisassemblerTables &tables, InstructionContext RecognizableInstr::insnContext() const { InstructionContext insnContext; - if (HasVEX_4VPrefix || HasVEXPrefix) { - if (HasVEX_LPrefix && HasVEX_WPrefix) - llvm_unreachable("Don't support VEX.L and VEX.W together"); - else if (HasOpSizePrefix && HasVEX_LPrefix) + if (HasVEX_4VPrefix || HasVEX_4VOp3Prefix|| HasVEXPrefix) { + if (HasVEX_LPrefix && HasVEX_WPrefix) { + if (HasOpSizePrefix) + insnContext = IC_VEX_L_W_OPSIZE; + else + llvm_unreachable("Don't support VEX.L and VEX.W together"); + } else if (HasOpSizePrefix && HasVEX_LPrefix) insnContext = IC_VEX_L_OPSIZE; else if (HasOpSizePrefix && HasVEX_WPrefix) insnContext = IC_VEX_W_OPSIZE; else if (HasOpSizePrefix) insnContext = IC_VEX_OPSIZE; - else if (HasVEX_LPrefix && Prefix == X86Local::XS) + else if (HasVEX_LPrefix && + (Prefix == X86Local::XS || Prefix == X86Local::T8XS)) insnContext = IC_VEX_L_XS; - else if (HasVEX_LPrefix && Prefix == X86Local::XD) + else if (HasVEX_LPrefix && (Prefix == X86Local::XD || + Prefix == X86Local::T8XD || + Prefix == X86Local::TAXD)) insnContext = IC_VEX_L_XD; - else if (HasVEX_WPrefix && Prefix == X86Local::XS) + else if (HasVEX_WPrefix && + (Prefix == X86Local::XS || Prefix == X86Local::T8XS)) insnContext = IC_VEX_W_XS; - else if (HasVEX_WPrefix && Prefix == X86Local::XD) + else if (HasVEX_WPrefix && (Prefix == X86Local::XD || + Prefix == X86Local::T8XD || + Prefix == X86Local::TAXD)) insnContext = IC_VEX_W_XD; else if (HasVEX_WPrefix) insnContext = IC_VEX_W; else if (HasVEX_LPrefix) insnContext = IC_VEX_L; - else if (Prefix == X86Local::XD) + else if (Prefix == X86Local::XD || Prefix == X86Local::T8XD || + Prefix == X86Local::TAXD) insnContext = IC_VEX_XD; - else if (Prefix == X86Local::XS) + else if (Prefix == X86Local::XS || Prefix == X86Local::T8XS) insnContext = IC_VEX_XS; else insnContext = IC_VEX; } else if (Is64Bit || HasREX_WPrefix) { if (HasREX_WPrefix && HasOpSizePrefix) insnContext = IC_64BIT_REXW_OPSIZE; - else if (HasOpSizePrefix && - (Prefix == X86Local::XD || Prefix == X86Local::TF)) + else if (HasOpSizePrefix && (Prefix == X86Local::XD || + Prefix == X86Local::T8XD || + Prefix == X86Local::TAXD)) insnContext = IC_64BIT_XD_OPSIZE; - else if (HasOpSizePrefix && Prefix == X86Local::XS) + else if (HasOpSizePrefix && + (Prefix == X86Local::XS || Prefix == X86Local::T8XS)) insnContext = IC_64BIT_XS_OPSIZE; else if (HasOpSizePrefix) insnContext = IC_64BIT_OPSIZE; - else if (HasREX_WPrefix && Prefix == X86Local::XS) - insnContext = IC_64BIT_REXW_XS; + else if (HasAdSizePrefix) + insnContext = IC_64BIT_ADSIZE; else if (HasREX_WPrefix && - (Prefix == X86Local::XD || Prefix == X86Local::TF)) + (Prefix == X86Local::XS || Prefix == X86Local::T8XS)) + insnContext = IC_64BIT_REXW_XS; + else if (HasREX_WPrefix && (Prefix == X86Local::XD || + Prefix == X86Local::T8XD || + Prefix == X86Local::TAXD)) insnContext = IC_64BIT_REXW_XD; - else if (Prefix == X86Local::XD || Prefix == X86Local::TF) + else if (Prefix == X86Local::XD || Prefix == X86Local::T8XD || + Prefix == X86Local::TAXD) insnContext = IC_64BIT_XD; - else if (Prefix == X86Local::XS) + else if (Prefix == X86Local::XS || Prefix == X86Local::T8XS) insnContext = IC_64BIT_XS; else if (HasREX_WPrefix) insnContext = IC_64BIT_REXW; else insnContext = IC_64BIT; } else { - if (HasOpSizePrefix && - (Prefix == X86Local::XD || Prefix == X86Local::TF)) + if (HasOpSizePrefix && (Prefix == X86Local::XD || + Prefix == X86Local::T8XD || + Prefix == X86Local::TAXD)) insnContext = IC_XD_OPSIZE; - else if (HasOpSizePrefix && Prefix == X86Local::XS) + else if (HasOpSizePrefix && + (Prefix == X86Local::XS || Prefix == X86Local::T8XS)) insnContext = IC_XS_OPSIZE; else if (HasOpSizePrefix) insnContext = IC_OPSIZE; - else if (Prefix == X86Local::XD || Prefix == X86Local::TF) + else if (HasAdSizePrefix) + insnContext = IC_ADSIZE; + else if (Prefix == X86Local::XD || Prefix == X86Local::T8XD || + Prefix == X86Local::TAXD) insnContext = IC_XD; - else if (Prefix == X86Local::XS || Prefix == X86Local::REP) + else if (Prefix == X86Local::XS || Prefix == X86Local::T8XS || + Prefix == X86Local::REP) insnContext = IC_XS; else insnContext = IC; @@ -371,19 +405,12 @@ RecognizableInstr::filter_ret RecognizableInstr::filter() const { return FILTER_STRONG; - // Filter out artificial instructions + // Filter out artificial instructions but leave in the LOCK_PREFIX so it is + // printed as a separate "instruction". - if (Name.find("TAILJMP") != Name.npos || - Name.find("_Int") != Name.npos || - Name.find("_int") != Name.npos || + if (Name.find("_Int") != Name.npos || Name.find("Int_") != Name.npos || Name.find("_NOREX") != Name.npos || - Name.find("_TC") != Name.npos || - Name.find("EH_RETURN") != Name.npos || - Name.find("V_SET") != Name.npos || - Name.find("LOCK_") != Name.npos || - Name.find("WIN") != Name.npos || - Name.find("_AVX") != Name.npos || Name.find("2SDL") != Name.npos) return FILTER_STRONG; @@ -421,12 +448,6 @@ RecognizableInstr::filter_ret RecognizableInstr::filter() const { Name.find("Xrr") != Name.npos || Name.find("rr64") != Name.npos) return FILTER_WEAK; - - if (Name == "VMASKMOVDQU64" || - Name == "VEXTRACTPSrr64" || - Name == "VMOVQd64rr" || - Name == "VMOVQs64rr") - return FILTER_WEAK; // Special cases. @@ -441,29 +462,15 @@ RecognizableInstr::filter_ret RecognizableInstr::filter() const { return FILTER_WEAK; if (Name.find("Fs") != Name.npos) return FILTER_WEAK; - if (Name == "MOVLPDrr" || - Name == "MOVLPSrr" || - Name == "PUSHFQ" || - Name == "BSF16rr" || - Name == "BSF16rm" || - Name == "BSR16rr" || - Name == "BSR16rm" || - Name == "MOVSX16rm8" || - Name == "MOVSX16rr8" || - Name == "MOVZX16rm8" || - Name == "MOVZX16rr8" || - Name == "PUSH32i16" || - Name == "PUSH64i16" || + if (Name == "PUSH64i16" || Name == "MOVPQI2QImr" || Name == "VMOVPQI2QImr" || - Name == "MOVSDmr" || - Name == "MOVSDrm" || - Name == "MOVSSmr" || - Name == "MOVSSrm" || Name == "MMX_MOVD64rrv164" || - Name == "CRC32m16" || Name == "MOV64ri64i32" || - Name == "CRC32r16") + Name == "VMASKMOVDQU64" || + Name == "VEXTRACTPSrr64" || + Name == "VMOVQd64rr" || + Name == "VMOVQs64rr") return FILTER_WEAK; if (HasFROperands && Name.find("MOV") != Name.npos && @@ -566,7 +573,7 @@ void RecognizableInstr::emitInstructionSpecifier(DisassemblerTables &tables) { bool hasFROperands = false; - assert(numOperands < X86_MAX_OPERANDS && "X86_MAX_OPERANDS is not large enough"); + assert(numOperands <= X86_MAX_OPERANDS && "X86_MAX_OPERANDS is not large enough"); for (operandIndex = 0; operandIndex < numOperands; ++operandIndex) { if (OperandList[operandIndex].Constraints.size()) { @@ -684,31 +691,40 @@ void RecognizableInstr::emitInstructionSpecifier(DisassemblerTables &tables) { // - In AVX, there is a register operand in the VEX.vvvv field here - // Operand 3 (optional) is an immediate. - if (HasVEX_4VPrefix) - assert(numPhysicalOperands >= 3 && numPhysicalOperands <= 4 && + if (HasVEX_4VPrefix || HasVEX_4VOp3Prefix) + assert(numPhysicalOperands >= 3 && numPhysicalOperands <= 5 && "Unexpected number of operands for MRMSrcRegFrm with VEX_4V"); else assert(numPhysicalOperands >= 2 && numPhysicalOperands <= 3 && "Unexpected number of operands for MRMSrcRegFrm"); HANDLE_OPERAND(roRegister) - + if (HasVEX_4VPrefix) // FIXME: In AVX, the register below becomes the one encoded // in ModRMVEX and the one above the one in the VEX.VVVV field HANDLE_OPERAND(vvvvRegister) - + + if (HasMemOp4Prefix) + HANDLE_OPERAND(immediate) + HANDLE_OPERAND(rmRegister) - HANDLE_OPTIONAL(immediate) + + if (HasVEX_4VOp3Prefix) + HANDLE_OPERAND(vvvvRegister) + + if (!HasMemOp4Prefix) + HANDLE_OPTIONAL(immediate) + HANDLE_OPTIONAL(immediate) // above might be a register in 7:4 break; case X86Local::MRMSrcMem: // Operand 1 is a register operand in the Reg/Opcode field. // Operand 2 is a memory operand (possibly SIB-extended) // - In AVX, there is a register operand in the VEX.vvvv field here - // Operand 3 (optional) is an immediate. - - if (HasVEX_4VPrefix) - assert(numPhysicalOperands >= 3 && numPhysicalOperands <= 4 && + + if (HasVEX_4VPrefix || HasVEX_4VOp3Prefix) + assert(numPhysicalOperands >= 3 && numPhysicalOperands <= 5 && "Unexpected number of operands for MRMSrcMemFrm with VEX_4V"); else assert(numPhysicalOperands >= 2 && numPhysicalOperands <= 3 && @@ -721,8 +737,17 @@ void RecognizableInstr::emitInstructionSpecifier(DisassemblerTables &tables) { // in ModRMVEX and the one above the one in the VEX.VVVV field HANDLE_OPERAND(vvvvRegister) + if (HasMemOp4Prefix) + HANDLE_OPERAND(immediate) + HANDLE_OPERAND(memory) - HANDLE_OPTIONAL(immediate) + + if (HasVEX_4VOp3Prefix) + HANDLE_OPERAND(vvvvRegister) + + if (!HasMemOp4Prefix) + HANDLE_OPTIONAL(immediate) + HANDLE_OPTIONAL(immediate) // above might be a register in 7:4 break; case X86Local::MRM0r: case X86Local::MRM1r: @@ -736,12 +761,12 @@ void RecognizableInstr::emitInstructionSpecifier(DisassemblerTables &tables) { // Operand 2 (optional) is an immediate or relocation. if (HasVEX_4VPrefix) assert(numPhysicalOperands <= 3 && - "Unexpected number of operands for MRMSrcMemFrm with VEX_4V"); + "Unexpected number of operands for MRMnRFrm with VEX_4V"); else assert(numPhysicalOperands <= 2 && "Unexpected number of operands for MRMnRFrm"); if (HasVEX_4VPrefix) - HANDLE_OPERAND(vvvvRegister); + HANDLE_OPERAND(vvvvRegister) HANDLE_OPTIONAL(rmRegister) HANDLE_OPTIONAL(relocation) break; @@ -755,8 +780,14 @@ void RecognizableInstr::emitInstructionSpecifier(DisassemblerTables &tables) { case X86Local::MRM7m: // Operand 1 is a memory operand (possibly SIB-extended) // Operand 2 (optional) is an immediate or relocation. - assert(numPhysicalOperands >= 1 && numPhysicalOperands <= 2 && - "Unexpected number of operands for MRMnMFrm"); + if (HasVEX_4VPrefix) + assert(numPhysicalOperands >= 2 && numPhysicalOperands <= 3 && + "Unexpected number of operands for MRMnMFrm"); + else + assert(numPhysicalOperands >= 1 && numPhysicalOperands <= 2 && + "Unexpected number of operands for MRMnMFrm"); + if (HasVEX_4VPrefix) + HANDLE_OPERAND(vvvvRegister) HANDLE_OPERAND(memory) HANDLE_OPTIONAL(relocation) break; @@ -843,15 +874,50 @@ void RecognizableInstr::emitDecodePath(DisassemblerTables &tables) const { opcodeToSet = Opcode; break; case X86Local::T8: - case X86Local::TF: + case X86Local::T8XD: + case X86Local::T8XS: opcodeType = THREEBYTE_38; - if (needsModRMForDecode(Form)) - filter = new ModFilter(isRegFormat(Form)); - else - filter = new DumbFilter(); + switch (Opcode) { + default: + if (needsModRMForDecode(Form)) + filter = new ModFilter(isRegFormat(Form)); + else + filter = new DumbFilter(); + break; +#define EXTENSION_TABLE(n) case 0x##n: + THREE_BYTE_38_EXTENSION_TABLES +#undef EXTENSION_TABLE + switch (Form) { + default: + llvm_unreachable("Unhandled two-byte extended opcode"); + case X86Local::MRM0r: + case X86Local::MRM1r: + case X86Local::MRM2r: + case X86Local::MRM3r: + case X86Local::MRM4r: + case X86Local::MRM5r: + case X86Local::MRM6r: + case X86Local::MRM7r: + filter = new ExtendedFilter(true, Form - X86Local::MRM0r); + break; + case X86Local::MRM0m: + case X86Local::MRM1m: + case X86Local::MRM2m: + case X86Local::MRM3m: + case X86Local::MRM4m: + case X86Local::MRM5m: + case X86Local::MRM6m: + case X86Local::MRM7m: + filter = new ExtendedFilter(false, Form - X86Local::MRM0m); + break; + MRM_MAPPING + } // switch (Form) + break; + } // switch (Opcode) opcodeToSet = Opcode; break; case X86Local::P_TA: + case X86Local::TAXD: opcodeType = THREEBYTE_3A; if (needsModRMForDecode(Form)) filter = new ModFilter(isRegFormat(Form)); @@ -1049,6 +1115,7 @@ OperandType RecognizableInstr::typeFromString(const std::string &s, TYPE("i16imm_pcrel", TYPE_REL16) TYPE("i32imm_pcrel", TYPE_REL32) TYPE("SSECC", TYPE_IMM3) + TYPE("AVXCC", TYPE_IMM5) TYPE("brtarget", TYPE_RELv) TYPE("uncondbrtarget", TYPE_RELv) TYPE("brtarget8", TYPE_REL8) @@ -1090,6 +1157,7 @@ OperandEncoding RecognizableInstr::immediateEncodingFromString ENCODING("i32i8imm", ENCODING_IB) ENCODING("u32u8imm", ENCODING_IB) ENCODING("SSECC", ENCODING_IB) + ENCODING("AVXCC", ENCODING_IB) ENCODING("i16imm", ENCODING_Iv) ENCODING("i16i8imm", ENCODING_IB) ENCODING("i32imm", ENCODING_Iv) diff --git a/utils/TableGen/X86RecognizableInstr.h b/utils/TableGen/X86RecognizableInstr.h index 4441597..6c0a234 100644 --- a/utils/TableGen/X86RecognizableInstr.h +++ b/utils/TableGen/X86RecognizableInstr.h @@ -50,17 +50,23 @@ private: uint8_t SegOvr; /// The hasOpSizePrefix field from the record bool HasOpSizePrefix; + /// The hasAdSizePrefix field from the record + bool HasAdSizePrefix; /// The hasREX_WPrefix field from the record bool HasREX_WPrefix; /// The hasVEXPrefix field from the record bool HasVEXPrefix; /// The hasVEX_4VPrefix field from the record bool HasVEX_4VPrefix; + /// The hasVEX_4VOp3Prefix field from the record + bool HasVEX_4VOp3Prefix; /// The hasVEX_WPrefix field from the record bool HasVEX_WPrefix; /// Inferred from the operands; indicates whether the L bit in the VEX prefix is set bool HasVEX_LPrefix; - // The ignoreVEX_L field from the record + /// The hasMemOp4Prefix field from the record + bool HasMemOp4Prefix; + /// The ignoreVEX_L field from the record bool IgnoresVEX_L; /// The hasLockPrefix field from the record bool HasLockPrefix; @@ -70,7 +76,7 @@ private: bool Is64Bit; // Whether the instruction has the predicate "In32BitMode" bool Is32Bit; - + /// The instruction name as listed in the tables std::string Name; /// The AT&T AsmString for the instruction diff --git a/utils/buildit/GNUmakefile b/utils/buildit/GNUmakefile index 470ee76..fc5578a 100644 --- a/utils/buildit/GNUmakefile +++ b/utils/buildit/GNUmakefile @@ -32,7 +32,7 @@ DSTROOT = $(OBJROOT)/../dst ####################################################################### -PREFIX = /Developer/usr/local +PREFIX = /usr/local # Unless assertions are forced on in the GMAKE command line, disable them. ifndef ENABLE_ASSERTIONS @@ -46,9 +46,6 @@ else LLVM_OPTIMIZED := yes endif -# Default to not install libLTO.dylib. -INSTALL_LIBLTO := no - # Default to do a native build, not a cross-build for an ARM host or simulator. ARM_HOSTED_BUILD := no IOS_SIM_BUILD := no @@ -66,7 +63,7 @@ install: $(OBJROOT) $(SYMROOT) $(DSTROOT) cd $(OBJROOT) && \ $(SRC)/utils/buildit/build_llvm "$(RC_ARCHS)" "$(TARGETS)" \ $(SRC) $(PREFIX) $(DSTROOT) $(SYMROOT) \ - $(ENABLE_ASSERTIONS) $(LLVM_OPTIMIZED) $(INSTALL_LIBLTO) \ + $(ENABLE_ASSERTIONS) $(LLVM_OPTIMIZED) \ $(ARM_HOSTED_BUILD) $(IOS_SIM_BUILD) \ $(RC_ProjectSourceVersion) $(RC_ProjectSourceSubversion) @@ -82,7 +79,7 @@ EmbeddedSim: Embedded: ARM_PLATFORM=`xcodebuild -version -sdk iphoneos PlatformPath` && \ - $(MAKE) DSTROOT=$(DSTROOT)$$ARM_PLATFORM install + $(MAKE) DSTROOT=$(DSTROOT)$$ARM_PLATFORM/Developer install # installhdrs does nothing, because the headers aren't useful until # the compiler is installed. diff --git a/utils/buildit/build_llvm b/utils/buildit/build_llvm index 0ffbc19..88a26d3 100755 --- a/utils/buildit/build_llvm +++ b/utils/buildit/build_llvm @@ -42,21 +42,17 @@ LLVM_ASSERTIONS="$7" # build. LLVM_OPTIMIZED="$8" -# The ninth parameter is a yes/no that indicates whether libLTO.dylib -# should be installed. -INSTALL_LIBLTO="$9" - # A yes/no parameter that controls whether to cross-build for an ARM host. -ARM_HOSTED_BUILD="${10}" +ARM_HOSTED_BUILD="$9" # A yes/no parameter that controls whether to cross-build for the iOS simulator -IOS_SIM_BUILD="${11}" +IOS_SIM_BUILD="${10}" # The version number of the submission, e.g. 1007. -LLVM_SUBMIT_VERSION="${12}" +LLVM_SUBMIT_VERSION="${11}" # The subversion number of the submission, e.g. 03. -LLVM_SUBMIT_SUBVERSION="${13}" +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 @@ -117,7 +113,15 @@ elif [ "$IOS_SIM_BUILD" = yes ]; then configure_opts="--enable-targets=x86 --host=i686-apple-darwin_sim \ --build=i686-apple-darwin10" else - configure_opts="--enable-targets=arm,x86,cbe" + configure_opts="--enable-targets=arm,x86" +fi + +if [ "$ARM_HOSTED_BUILD" != yes ]; then + if [ $SDKROOT ]; then + CPPFLAGS="$CPPFLAGS -isysroot $SDKROOT" + fi + for host in $HOSTS; do :; done + CPPFLAGS="$CPPFLAGS -arch $host" fi if [ \! -f Makefile.config ]; then @@ -125,6 +129,7 @@ if [ \! -f Makefile.config ]; then --enable-assertions=$LLVM_ASSERTIONS \ --enable-optimized=$LLVM_OPTIMIZED \ --disable-bindings \ + CPPFLAGS="$CPPFLAGS" \ || exit 1 fi @@ -156,6 +161,7 @@ make $JOBS_FLAG $OPTIMIZE_OPTS UNIVERSAL=1 UNIVERSAL_ARCH="$HOSTS" \ UNIVERSAL_SDK_PATH=$SDKROOT \ 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 \ @@ -218,9 +224,6 @@ if [ "x$LLVM_DEBUG" != "x1" ]; then done fi -# Copy over the tblgen utility. -cp `find $DIR -name tblgen` $DEST_DIR$DEST_ROOT/bin - # Remove .dir files cd $DEST_DIR$DEST_ROOT rm -f bin/.dir etc/llvm/.dir lib/.dir @@ -289,34 +292,11 @@ find obj-* -name \*.\[chy\] -o -name \*.cpp -print \ | cpio -pdml $SYM_DIR/src || exit 1 ################################################################################ -# Install and strip libLTO.dylib +# Remove libLTO.dylib and lto.h. Those are installed by clang. cd $DEST_DIR$DEST_ROOT -if [ "$INSTALL_LIBLTO" = "yes" ]; then - DT_HOME="$DEST_DIR/Developer/usr" - mkdir -p $DT_HOME/lib - mv lib/libLTO.dylib $DT_HOME/lib/libLTO.dylib - - # Save a copy of the unstripped dylib - mkdir -p $SYM_DIR/Developer/usr/lib - cp $DT_HOME/lib/libLTO.dylib $SYM_DIR/Developer/usr/lib/libLTO.dylib - - # Use '-l' to strip i386 modules. N.B. that flag doesn't work with kext or - # PPC objects! - $STRIP -arch all -Sl $DT_HOME/lib/libLTO.dylib - - if [ "x$DISABLE_USR_LINKS" == "x" ]; then - # Add a symlink in /usr/lib for B&I. - mkdir -p $DEST_DIR/usr/lib/ - (cd $DEST_DIR/usr/lib && \ - ln -s ../../Developer/usr/lib/libLTO.dylib ./libLTO.dylib) - fi -else - rm -f lib/libLTO.dylib -fi +rm -f lib/libLTO.dylib rm -f lib/libLTO.a lib/libLTO.la - -# Omit lto.h from the result. Clang will supply. find $DEST_DIR$DEST_ROOT -name lto.h -delete ################################################################################ diff --git a/utils/cgiplotNLT.pl b/utils/cgiplotNLT.pl deleted file mode 100755 index 0360e41..0000000 --- a/utils/cgiplotNLT.pl +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/perl -#takes a test and a program from a dp and produces a gnuplot script -#use like perl plotNLT.pl password Programs/MultiSource/Benchmarks/ASCI_Purple/SMG2000/smg2000 llc - -use CGI; -use DBI; -my $q = new CGI; - -# database information -$db="llvmalpha"; -$host="localhost"; -$userid="llvmdbuser"; -$passwd=$q->param('pwd'); -$connectionInfo="dbi:mysql:$db;$host"; - -# make connection to database -$dbh = DBI->connect($connectionInfo,$userid,$passwd) or die DBI->errstr; - - -$count = 0; -while ($q->param('n' . $count)) - { - $count++; - } - -$| = 1; -print "Content-type: image/png", "\n\n"; - -open CMDSTREAM, "|gnuplot"; -#open CMDSTREAM, "|echo"; - -print CMDSTREAM "set terminal png\n"; -print CMDSTREAM "set output\n"; -print CMDSTREAM "set xdata time\n"; -print CMDSTREAM 'set timefmt "%Y-%m-%d"'; -print CMDSTREAM "\nplot"; -for ($iter = 0; $iter < $count; $iter++) { - if ($iter) - { print CMDSTREAM ","; } - print CMDSTREAM " '-' using 1:2 title \"" . $q->param('t' . $iter) . "," . $q->param('n' . $iter) . "\"with lines"; -} - -print CMDSTREAM "\n"; - -for ($iter = 0; $iter < $count; $iter++) { - - $prog = $q->param('n' . $iter); - $test = $q->param('t' . $iter); - - $query = "Select RUN, VALUE from Tests where TEST = '$test' AND NAME = '$prog' ORDER BY RUN"; - #print "\n$query\n"; - - my $sth = $dbh->prepare( $query) || die "Can't prepare statement: $DBI::errstr";; - - my $rc = $sth->execute or die DBI->errstr; - - while(($da,$v) = $sth->fetchrow_array) - { - print CMDSTREAM "$da $v\n"; - } - - print CMDSTREAM "e\n"; -} -print CMDSTREAM "exit\n"; -close CMDSTREAM; - -# disconnect from database -$dbh->disconnect; diff --git a/utils/clang-parse-diagnostics-file b/utils/clang-parse-diagnostics-file new file mode 100755 index 0000000..b8ea8ea --- /dev/null +++ b/utils/clang-parse-diagnostics-file @@ -0,0 +1,78 @@ +#!/usr/bin/env python + +import plistlib + +def main(): + from optparse import OptionParser, OptionGroup + parser = OptionParser("""\ +Usage: %prog [options] <path> + +Utility for dumping Clang-style logged diagnostics.\ +""") + parser.add_option("-a", "--all", action="store_true", dest="all", + default=False, help="dump all messages.") + parser.add_option("-e", "--error", action="store_true", dest="error", + default=False, help="dump 'error' messages.") + parser.add_option("-f", "--fatal", action="store_true", dest="fatal", + default=False, help="dump 'fatal error' messages.") + parser.add_option("-i", "--ignored", action="store_true", dest="ignored", + default=False, help="dump 'ignored' messages.") + parser.add_option("-n", "--note", action="store_true", dest="note", + default=False, help="dump 'note' messages.") + parser.add_option("-w", "--warning", action="store_true", dest="warning", + default=False, help="dump 'warning' messages.") + (opts, args) = parser.parse_args() + + if len(args) != 1: + parser.error("invalid number of arguments") + + levels = {'error': False, 'fatal error': False, 'ignored': False, + 'note': False, 'warning': False} + if opts.error: + levels['error'] = True + if opts.fatal: + levels['fatal error'] = True + if opts.ignored: + levels['ignored'] = True + if opts.note: + levels['note'] = True + if opts.warning: + levels['warning'] = True + + path, = args + + # Read the diagnostics log. + f = open(path) + try: + data = f.read() + finally: + f.close() + + # Complete the plist (the log itself is just the chunks). + data = """\ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" \ + "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<array> +%s +</array> +</plist>""" % data + + # Load the diagnostics. + diags = plistlib.readPlistFromString(data) + + # Print out the diagnostics. + print + print "**** BUILD DIAGNOSTICS ****" + for i, file_diags in enumerate(diags): + file = file_diags.get('main-file') + print "*** %s ***" % file + for d in file_diags.get('diagnostics', ()): + if levels[d.get('level')] or opts.all: + print " %s:%s:%s: %s: %s" % ( + d.get('filename'), d.get('line'), d.get('column'), + d.get('level'), d.get('message')) + +if __name__ == "__main__": + main() diff --git a/utils/emacs/tablegen-mode.el b/utils/emacs/tablegen-mode.el index 3853ce6..e83a34c 100644 --- a/utils/emacs/tablegen-mode.el +++ b/utils/emacs/tablegen-mode.el @@ -13,7 +13,7 @@ (defvar tablegen-font-lock-keywords (let ((kw (regexp-opt '("class" "defm" "def" "field" "include" "in" - "let" "multiclass") + "let" "multiclass" "foreach") 'words)) (type-kw (regexp-opt '("bit" "bits" "code" "dag" "int" "list" "string") 'words)) diff --git a/utils/importNLT.pl b/utils/importNLT.pl deleted file mode 100644 index c1b950d..0000000 --- a/utils/importNLT.pl +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/perl -#take the output of parseNLT.pl and load it into a database -# use like: cat file |perl parseNLT.pl |perl importNLT.pl password - -use DBI; - -# database information -$db="llvmalpha"; -$host="localhost"; -$userid="llvmdbuser"; -$passwd=shift @ARGV; -$connectionInfo="dbi:mysql:$db;$host"; - -# make connection to database -$dbh = DBI->connect($connectionInfo,$userid,$passwd) or die DBI->errstr; -my $sth = $dbh->prepare( q{ - INSERT INTO Tests (NAME, RUN, TEST, VALUE) VALUES (?, STR_TO_DATE(?, '\%d \%M \%Y'), ?, ?) - }) || die "Can't prepare statement: $DBI::errstr";; - -while($d = <>) -{ - chomp $d; - if (18 == scalar split " ", $d) - { - ($day, $mon, $year, $prog, $gccas, $bc, $llccompile, $llcbetacompile, $jitcompile, - $mc, $gcc, $cbe, $llc, $llcbeta, $jit, $foo1, $foo2, $foo3) = split " ", $d; - if ($gccas =~ /\d+/) - { - $dbh->do("INSERT INTO Tests (NAME, RUN, TEST, VALUE) VALUES - ('$prog', STR_TO_DATE('$day $mon $year', '\%d \%M \%Y'), 'gccas', $gccas)") || die DBI->errstr; - } - if ($bc =~ /\d/) - { - $dbh->do("INSERT INTO Tests (NAME, RUN, TEST, VALUE) VALUES - ('$prog', STR_TO_DATE('$day $mon $year', '\%d \%M \%Y'), 'bytecode', $bc)") || die DBI->errstr; - } - if ($llccompile =~ /\d/) - { - $dbh->do("INSERT INTO Tests (NAME, RUN, TEST, VALUE) VALUES - ('$prog', STR_TO_DATE('$day $mon $year', '\%d \%M \%Y'), 'llc-compile', $llccompile)") || die DBI->errstr; - } - if ($llcbetacompile =~ /\d/) - { - $dbh->do("INSERT INTO Tests (NAME, RUN, TEST, VALUE) VALUES - ('$prog', STR_TO_DATE('$day $mon $year', '\%d \%M \%Y'), 'llc-beta-compile', $llcbetacompile)") || die DBI->errstr; - } - if ($jitcompile =~ /\d/) - { - $dbh->do("INSERT INTO Tests (NAME, RUN, TEST, VALUE) VALUES - ('$prog', STR_TO_DATE('$day $mon $year', '\%d \%M \%Y'), 'jit-compile', $jitcompile)") || die DBI->errstr; - } - if ($mc =~ /\d/) - { - $dbh->do("INSERT INTO Tests (NAME, RUN, TEST, VALUE) VALUES - ('$prog', STR_TO_DATE('$day $mon $year', '\%d \%M \%Y'), 'machine-code', $mc)") || die DBI->errstr; - } - if ($gcc =~ /\d/) - { - $dbh->do("INSERT INTO Tests (NAME, RUN, TEST, VALUE) VALUES - ('$prog', STR_TO_DATE('$day $mon $year', '\%d \%M \%Y'), 'gcc', $gcc)") || die DBI->errstr; - } - if ($llc =~ /\d/) - { - $dbh->do("INSERT INTO Tests (NAME, RUN, TEST, VALUE) VALUES - ('$prog', STR_TO_DATE('$day $mon $year', '\%d \%M \%Y'), 'llc', $llc)") || die DBI->errstr; - } - if ($llcbeta =~ /\d/) - { - $dbh->do("INSERT INTO Tests (NAME, RUN, TEST, VALUE) VALUES - ('$prog', STR_TO_DATE('$day $mon $year', '\%d \%M \%Y'), 'llc-beta', $llcbeta)") || die DBI->errstr; - } - if ($jit =~ /\d/) - { - $dbh->do("INSERT INTO Tests (NAME, RUN, TEST, VALUE) VALUES - ('$prog', STR_TO_DATE('$day $mon $year', '\%d \%M \%Y'), 'jit', $jit)") || die DBI->errstr; - } - print "."; - } - else - { - print "\nNO: $d\n"; - } -} -print "\n"; -# disconnect from database -$dbh->disconnect; diff --git a/utils/json-bench/CMakeLists.txt b/utils/json-bench/CMakeLists.txt new file mode 100644 index 0000000..03ac51c --- /dev/null +++ b/utils/json-bench/CMakeLists.txt @@ -0,0 +1,5 @@ +add_llvm_utility(json-bench + JSONBench.cpp + ) + +target_link_libraries(json-bench LLVMSupport) diff --git a/utils/json-bench/JSONBench.cpp b/utils/json-bench/JSONBench.cpp new file mode 100644 index 0000000..ca8a36a --- /dev/null +++ b/utils/json-bench/JSONBench.cpp @@ -0,0 +1,85 @@ +//===- JSONBench - Benchmark the JSONParser implementation ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This program executes the JSONParser on differntly sized JSON texts and +// outputs the run time. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/Twine.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/JSONParser.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" + +static llvm::cl::opt<bool> +Verify("verify", llvm::cl::desc( + "Run a quick verification useful for regression testing"), + llvm::cl::init(false)); + +static llvm::cl::opt<unsigned> +MemoryLimitMB("memory-limit", llvm::cl::desc( + "Do not use more megabytes of memory"), + llvm::cl::init(1000)); + +void benchmark(llvm::TimerGroup &Group, llvm::StringRef Name, + llvm::StringRef JSONText) { + llvm::Timer BaseLine((Name + ": Loop").str(), Group); + BaseLine.startTimer(); + char C = 0; + for (llvm::StringRef::iterator I = JSONText.begin(), + E = JSONText.end(); + I != E; ++I) { C += *I; } + BaseLine.stopTimer(); + volatile char DontOptimizeOut = C; (void)DontOptimizeOut; + + llvm::Timer Parsing((Name + ": Parsing").str(), Group); + Parsing.startTimer(); + llvm::SourceMgr SM; + llvm::JSONParser Parser(JSONText, &SM); + if (!Parser.validate()) { + llvm::errs() << "Parsing error in JSON parser benchmark.\n"; + exit(1); + } + Parsing.stopTimer(); +} + +std::string createJSONText(size_t MemoryMB, unsigned ValueSize) { + std::string JSONText; + llvm::raw_string_ostream Stream(JSONText); + Stream << "[\n"; + size_t MemoryBytes = MemoryMB * 1024 * 1024; + while (JSONText.size() < MemoryBytes) { + Stream << " {\n" + << " \"key1\": \"" << std::string(ValueSize, '*') << "\",\n" + << " \"key2\": \"" << std::string(ValueSize, '*') << "\",\n" + << " \"key3\": \"" << std::string(ValueSize, '*') << "\"\n" + << " }"; + Stream.flush(); + if (JSONText.size() < MemoryBytes) Stream << ","; + Stream << "\n"; + } + Stream << "]\n"; + Stream.flush(); + return JSONText; +} + +int main(int argc, char **argv) { + llvm::cl::ParseCommandLineOptions(argc, argv); + llvm::TimerGroup Group("JSON parser benchmark"); + if (Verify) { + benchmark(Group, "Fast", createJSONText(10, 500)); + } else { + benchmark(Group, "Small Values", createJSONText(MemoryLimitMB, 5)); + benchmark(Group, "Medium Values", createJSONText(MemoryLimitMB, 500)); + benchmark(Group, "Large Values", createJSONText(MemoryLimitMB, 50000)); + } + return 0; +} + diff --git a/utils/json-bench/Makefile b/utils/json-bench/Makefile new file mode 100644 index 0000000..6651626 --- /dev/null +++ b/utils/json-bench/Makefile @@ -0,0 +1,21 @@ +##===- 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 = json-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 + diff --git a/utils/lit/lit/ExampleTests/LLVM.InTree/test/lit.cfg b/utils/lit/lit/ExampleTests/LLVM.InTree/test/lit.cfg index e7ef037..e9df1e5 100644 --- a/utils/lit/lit/ExampleTests/LLVM.InTree/test/lit.cfg +++ b/utils/lit/lit/ExampleTests/LLVM.InTree/test/lit.cfg @@ -75,16 +75,6 @@ for line in open(os.path.join(config.llvm_obj_root, 'test', 'site.exp')): if m: site_exp[m.group(1)] = m.group(2) -# Add substitutions. -for sub in ['prcontext', 'llvmgcc', 'llvmgxx', 'compile_cxx', 'compile_c', - 'link', 'shlibext', 'ocamlopt', 'llvmdsymutil', 'llvmlibsdir', - 'bugpoint_topts']: - if sub in ('llvmgcc', 'llvmgxx'): - config.substitutions.append(('%' + sub, - site_exp[sub] + ' -emit-llvm -w')) - else: - config.substitutions.append(('%' + sub, site_exp[sub])) - excludes = [] # Provide target_triple for use in XFAIL and XTARGET. @@ -95,10 +85,6 @@ targets = set(site_exp["TARGETS_TO_BUILD"].split()) def llvm_supports_target(name): return name in targets -langs = set(site_exp['llvmgcc_langs'].split(',')) -def llvm_gcc_supports(name): - return name in langs - # Provide on_clone hook for reading 'dg.exp'. import os simpleLibData = re.compile(r"""load_lib llvm.exp diff --git a/utils/lit/lit/ExampleTests/LLVM.InTree/test/site.exp b/utils/lit/lit/ExampleTests/LLVM.InTree/test/site.exp index efa839e..4bc58d7 100644 --- a/utils/lit/lit/ExampleTests/LLVM.InTree/test/site.exp +++ b/utils/lit/lit/ExampleTests/LLVM.InTree/test/site.exp @@ -2,27 +2,9 @@ # Do not edit here. If you wish to override these values # edit the last section set target_triplet "x86_64-apple-darwin10" -set TARGETS_TO_BUILD "X86 Sparc PowerPC Alpha ARM Mips CellSPU PIC16 XCore MSP430 SystemZ Blackfin CBackend MSIL CppBackend" -set llvmgcc_langs "c,c++,objc,obj-c++" -set prcontext "/usr/bin/tclsh8.4 /Volumes/Data/ddunbar/llvm/test/Scripts/prcontext.tcl" -set llvmtoolsdir "/Users/ddunbar/llvm.obj.64/Debug/bin" -set llvmlibsdir "/Users/ddunbar/llvm.obj.64/Debug/lib" +set TARGETS_TO_BUILD "X86 Sparc PowerPC ARM Mips CellSPU PIC16 XCore MSP430 Blackfin MSIL CppBackend" set srcroot "/Volumes/Data/ddunbar/llvm" set objroot "/Volumes/Data/ddunbar/llvm.obj.64" set srcdir "/Volumes/Data/ddunbar/llvm/test" set objdir "/Volumes/Data/ddunbar/llvm.obj.64/test" -set gccpath "/usr/bin/gcc -arch x86_64" -set gxxpath "/usr/bin/g++ -arch x86_64" -set compile_c " /usr/bin/gcc -arch x86_64 -I/Users/ddunbar/llvm.obj.64/include -I/Users/ddunbar/llvm.obj.64/test -I/Volumes/Data/ddunbar/llvm.obj.64/include -I/Volumes/Data/ddunbar/llvm/include -I/Volumes/Data/ddunbar/llvm/test -D_DEBUG -D_GNU_SOURCE -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -m64 -pedantic -Wno-long-long -Wall -W -Wno-unused-parameter -Wwrite-strings -c " -set compile_cxx " /usr/bin/g++ -arch x86_64 -I/Users/ddunbar/llvm.obj.64/include -I/Users/ddunbar/llvm.obj.64/test -I/Volumes/Data/ddunbar/llvm.obj.64/include -I/Volumes/Data/ddunbar/llvm/include -I/Volumes/Data/ddunbar/llvm/test -D_DEBUG -D_GNU_SOURCE -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -g -fno-exceptions -fno-common -Woverloaded-virtual -m64 -pedantic -Wno-long-long -Wall -W -Wno-unused-parameter -Wwrite-strings -c " -set link " /usr/bin/g++ -arch x86_64 -I/Users/ddunbar/llvm.obj.64/include -I/Users/ddunbar/llvm.obj.64/test -I/Volumes/Data/ddunbar/llvm.obj.64/include -I/Volumes/Data/ddunbar/llvm/include -I/Volumes/Data/ddunbar/llvm/test -D_DEBUG -D_GNU_SOURCE -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -g -fno-exceptions -fno-common -Woverloaded-virtual -m64 -pedantic -Wno-long-long -Wall -W -Wno-unused-parameter -Wwrite-strings -g -L/Users/ddunbar/llvm.obj.64/Debug/lib -L/Volumes/Data/ddunbar/llvm.obj.64/Debug/lib " -set llvmgcc "/Users/ddunbar/llvm-gcc/install/bin/llvm-gcc -m64 " -set llvmgxx "/Users/ddunbar/llvm-gcc/install/bin/llvm-gcc -m64 " -set bugpoint_topts "-gcc-tool-args -m64" -set shlibext ".dylib" -set ocamlopt "/sw/bin/ocamlopt -cc \"g++ -Wall -D_FILE_OFFSET_BITS=64 -D_REENTRANT\" -I /Users/ddunbar/llvm.obj.64/Debug/lib/ocaml" -set valgrind "" -set grep "/usr/bin/grep" -set gas "/usr/bin/as" -set llvmdsymutil "dsymutil" ## All variables above are generated by configure. Do Not Edit ## diff --git a/utils/lit/lit/ExampleTests/LLVM.OutOfTree/obj/test/site.exp b/utils/lit/lit/ExampleTests/LLVM.OutOfTree/obj/test/site.exp index efa839e..4bc58d7 100644 --- a/utils/lit/lit/ExampleTests/LLVM.OutOfTree/obj/test/site.exp +++ b/utils/lit/lit/ExampleTests/LLVM.OutOfTree/obj/test/site.exp @@ -2,27 +2,9 @@ # Do not edit here. If you wish to override these values # edit the last section set target_triplet "x86_64-apple-darwin10" -set TARGETS_TO_BUILD "X86 Sparc PowerPC Alpha ARM Mips CellSPU PIC16 XCore MSP430 SystemZ Blackfin CBackend MSIL CppBackend" -set llvmgcc_langs "c,c++,objc,obj-c++" -set prcontext "/usr/bin/tclsh8.4 /Volumes/Data/ddunbar/llvm/test/Scripts/prcontext.tcl" -set llvmtoolsdir "/Users/ddunbar/llvm.obj.64/Debug/bin" -set llvmlibsdir "/Users/ddunbar/llvm.obj.64/Debug/lib" +set TARGETS_TO_BUILD "X86 Sparc PowerPC ARM Mips CellSPU PIC16 XCore MSP430 Blackfin MSIL CppBackend" set srcroot "/Volumes/Data/ddunbar/llvm" set objroot "/Volumes/Data/ddunbar/llvm.obj.64" set srcdir "/Volumes/Data/ddunbar/llvm/test" set objdir "/Volumes/Data/ddunbar/llvm.obj.64/test" -set gccpath "/usr/bin/gcc -arch x86_64" -set gxxpath "/usr/bin/g++ -arch x86_64" -set compile_c " /usr/bin/gcc -arch x86_64 -I/Users/ddunbar/llvm.obj.64/include -I/Users/ddunbar/llvm.obj.64/test -I/Volumes/Data/ddunbar/llvm.obj.64/include -I/Volumes/Data/ddunbar/llvm/include -I/Volumes/Data/ddunbar/llvm/test -D_DEBUG -D_GNU_SOURCE -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -m64 -pedantic -Wno-long-long -Wall -W -Wno-unused-parameter -Wwrite-strings -c " -set compile_cxx " /usr/bin/g++ -arch x86_64 -I/Users/ddunbar/llvm.obj.64/include -I/Users/ddunbar/llvm.obj.64/test -I/Volumes/Data/ddunbar/llvm.obj.64/include -I/Volumes/Data/ddunbar/llvm/include -I/Volumes/Data/ddunbar/llvm/test -D_DEBUG -D_GNU_SOURCE -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -g -fno-exceptions -fno-common -Woverloaded-virtual -m64 -pedantic -Wno-long-long -Wall -W -Wno-unused-parameter -Wwrite-strings -c " -set link " /usr/bin/g++ -arch x86_64 -I/Users/ddunbar/llvm.obj.64/include -I/Users/ddunbar/llvm.obj.64/test -I/Volumes/Data/ddunbar/llvm.obj.64/include -I/Volumes/Data/ddunbar/llvm/include -I/Volumes/Data/ddunbar/llvm/test -D_DEBUG -D_GNU_SOURCE -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -g -fno-exceptions -fno-common -Woverloaded-virtual -m64 -pedantic -Wno-long-long -Wall -W -Wno-unused-parameter -Wwrite-strings -g -L/Users/ddunbar/llvm.obj.64/Debug/lib -L/Volumes/Data/ddunbar/llvm.obj.64/Debug/lib " -set llvmgcc "/Users/ddunbar/llvm-gcc/install/bin/llvm-gcc -m64 " -set llvmgxx "/Users/ddunbar/llvm-gcc/install/bin/llvm-gcc -m64 " -set bugpoint_topts "-gcc-tool-args -m64" -set shlibext ".dylib" -set ocamlopt "/sw/bin/ocamlopt -cc \"g++ -Wall -D_FILE_OFFSET_BITS=64 -D_REENTRANT\" -I /Users/ddunbar/llvm.obj.64/Debug/lib/ocaml" -set valgrind "" -set grep "/usr/bin/grep" -set gas "/usr/bin/as" -set llvmdsymutil "dsymutil" ## All variables above are generated by configure. Do Not Edit ## diff --git a/utils/lit/lit/ExampleTests/LLVM.OutOfTree/src/test/lit.cfg b/utils/lit/lit/ExampleTests/LLVM.OutOfTree/src/test/lit.cfg index e7ef037..e9df1e5 100644 --- a/utils/lit/lit/ExampleTests/LLVM.OutOfTree/src/test/lit.cfg +++ b/utils/lit/lit/ExampleTests/LLVM.OutOfTree/src/test/lit.cfg @@ -75,16 +75,6 @@ for line in open(os.path.join(config.llvm_obj_root, 'test', 'site.exp')): if m: site_exp[m.group(1)] = m.group(2) -# Add substitutions. -for sub in ['prcontext', 'llvmgcc', 'llvmgxx', 'compile_cxx', 'compile_c', - 'link', 'shlibext', 'ocamlopt', 'llvmdsymutil', 'llvmlibsdir', - 'bugpoint_topts']: - if sub in ('llvmgcc', 'llvmgxx'): - config.substitutions.append(('%' + sub, - site_exp[sub] + ' -emit-llvm -w')) - else: - config.substitutions.append(('%' + sub, site_exp[sub])) - excludes = [] # Provide target_triple for use in XFAIL and XTARGET. @@ -95,10 +85,6 @@ targets = set(site_exp["TARGETS_TO_BUILD"].split()) def llvm_supports_target(name): return name in targets -langs = set(site_exp['llvmgcc_langs'].split(',')) -def llvm_gcc_supports(name): - return name in langs - # Provide on_clone hook for reading 'dg.exp'. import os simpleLibData = re.compile(r"""load_lib llvm.exp diff --git a/utils/lit/lit/LitConfig.py b/utils/lit/lit/LitConfig.py index 2cc2781..c71c0cc 100644 --- a/utils/lit/lit/LitConfig.py +++ b/utils/lit/lit/LitConfig.py @@ -61,6 +61,8 @@ class LitConfig: """load_config(config, path) - Load a config object from an alternate path.""" from TestingConfig import TestingConfig + if self.debug: + self.note('load_config from %r' % path) return TestingConfig.frompath(path, config.parent, self, mustExist = True, config = config) diff --git a/utils/lit/lit/TestRunner.py b/utils/lit/lit/TestRunner.py index f5f7c19..b5f7986 100644 --- a/utils/lit/lit/TestRunner.py +++ b/utils/lit/lit/TestRunner.py @@ -23,6 +23,56 @@ kUseCloseFDs = not kIsWindows # Use temporary files to replace /dev/null on Windows. kAvoidDevNull = kIsWindows +# Negate if win32file is not found. +kHaveWin32File = kIsWindows + +def RemoveForce(f): + try: + os.remove(f) + except OSError: + pass + +def WinWaitReleased(f): + global kHaveWin32File + if not kHaveWin32File: + return + try: + import time + import win32file, pywintypes + retry_cnt = 256 + while True: + try: + h = win32file.CreateFile( + f, + win32file.GENERIC_READ, + 0, # Exclusive + None, + win32file.OPEN_EXISTING, + win32file.FILE_ATTRIBUTE_NORMAL, + None) + h.close() + return + except WindowsError, (winerror, strerror): + retry_cnt = retry_cnt - 1 + if retry_cnt <= 0: + raise + elif winerror == 32: # ERROR_SHARING_VIOLATION + pass + else: + raise + except pywintypes.error, e: + retry_cnt = retry_cnt - 1 + if retry_cnt <= 0: + raise + elif e[0]== 32: # ERROR_SHARING_VIOLATION + pass + else: + raise + time.sleep(0.01) + except ImportError, e: + kHaveWin32File = False + return + def executeCommand(command, cwd=None, env=None): p = subprocess.Popen(command, cwd=cwd, stdin=subprocess.PIPE, @@ -115,6 +165,7 @@ def executeShCmd(cmd, cfg, cwd, results): else: if r[2] is None: if kAvoidDevNull and r[0] == '/dev/null': + r[0] = None r[2] = tempfile.TemporaryFile(mode=r[1]) else: r[2] = open(r[0], r[1]) @@ -123,7 +174,7 @@ def executeShCmd(cmd, cfg, cwd, results): # 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(r) result = r[2] final_redirects.append(result) @@ -185,7 +236,7 @@ def executeShCmd(cmd, cfg, cwd, results): # 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: - f.close() + f[2].close() # FIXME: There is probably still deadlock potential here. Yawn. procData = [None] * len(procs) @@ -224,12 +275,15 @@ def executeShCmd(cmd, cfg, cwd, results): else: exitCode = res + # Make sure opened_files is released by other (child) processes. + if kIsWindows: + for f in opened_files: + if f[0] is not None: + WinWaitReleased(f[0]) + # Remove any named temporary files we created. for f in named_temp_files: - try: - os.remove(f) - except OSError: - pass + RemoveForce(f) if cmd.negate: exitCode = not exitCode @@ -383,7 +437,8 @@ def isExpectedFail(xfails, xtargets, target_triple): return True -def parseIntegratedTestScript(test, normalize_slashes=False): +def parseIntegratedTestScript(test, normalize_slashes=False, + extra_substitutions=[]): """parseIntegratedTestScript - Scan an LLVM/Clang style integrated test script and extract the lines to 'RUN' as well as 'XFAIL' and 'XTARGET' information. The RUN lines also will have variable substitution performed. @@ -410,11 +465,13 @@ def parseIntegratedTestScript(test, normalize_slashes=False): tmpBase = tmpBase.replace('\\', '/') # We use #_MARKER_# to hide %% while we do the other substitutions. - substitutions = [('%%', '#_MARKER_#')] + substitutions = list(extra_substitutions) + substitutions.extend([('%%', '#_MARKER_#')]) substitutions.extend(test.config.substitutions) substitutions.extend([('%s', sourcepath), ('%S', sourcedir), ('%p', sourcedir), + ('%{pathsep}', os.pathsep), ('%t', tmpBase + '.tmp'), ('%T', tmpDir), # FIXME: Remove this once we kill DejaGNU. @@ -557,11 +614,12 @@ def executeTclTest(test, litConfig): return formatTestOutput(status, out, err, exitCode, failDueToStderr, script) -def executeShTest(test, litConfig, useExternalSh): +def executeShTest(test, litConfig, useExternalSh, + extra_substitutions=[]): if test.config.unsupported: return (Test.UNSUPPORTED, 'Test is unsupported') - res = parseIntegratedTestScript(test, useExternalSh) + res = parseIntegratedTestScript(test, useExternalSh, extra_substitutions) if len(res) == 2: return res diff --git a/utils/lit/lit/TestingConfig.py b/utils/lit/lit/TestingConfig.py index a92dca8..223120c 100644 --- a/utils/lit/lit/TestingConfig.py +++ b/utils/lit/lit/TestingConfig.py @@ -16,11 +16,12 @@ class TestingConfig: 'PATH' : os.pathsep.join(litConfig.path + [os.environ.get('PATH','')]), 'SYSTEMROOT' : os.environ.get('SYSTEMROOT',''), + 'LLVM_DISABLE_CRASH_REPORT' : '1', } if sys.platform == 'win32': environment.update({ - 'LLVM_DISABLE_CRT_DEBUG' : '1', + 'INCLUDE' : os.environ.get('INCLUDE',''), 'PATHEXT' : os.environ.get('PATHEXT',''), 'PYTHONUNBUFFERED' : '1', 'TEMP' : os.environ.get('TEMP',''), @@ -50,14 +51,19 @@ class TestingConfig: cfg_globals['__file__'] = path try: exec f in cfg_globals + if litConfig.debug: + litConfig.note('... loaded config %r' % path) except SystemExit,status: # We allow normal system exit inside a config file to just # return control without error. if status.args: raise f.close() - elif mustExist: - litConfig.fatal('unable to load config from %r ' % path) + else: + if mustExist: + litConfig.fatal('unable to load config from %r ' % path) + elif litConfig.debug: + litConfig.note('... config not found - %r' %path) config.finish(litConfig) return config @@ -108,3 +114,12 @@ class TestingConfig: # files. Should we distinguish them? self.test_source_root = str(self.test_source_root) self.excludes = set(self.excludes) + + @property + def root(self): + """root attribute - The root configuration for the test suite.""" + if self.parent is None: + return self + else: + return self.parent.root + diff --git a/utils/lit/lit/main.py b/utils/lit/lit/main.py index e1a380c..039868d 100755 --- a/utils/lit/lit/main.py +++ b/utils/lit/lit/main.py @@ -429,6 +429,10 @@ def main(builtinParameters = {}): # Bump the GIL check interval, its more imp group.add_option("", "--shuffle", dest="shuffle", help="Run tests in random order", action="store_true", default=False) + group.add_option("", "--filter", dest="filter", metavar="EXPRESSION", + help=("Only run tests with paths matching the given " + "regular expression"), + action="store", default=None) parser.add_option_group(group) group = OptionGroup(parser, "Debug and Experimental Options") @@ -452,9 +456,10 @@ def main(builtinParameters = {}): # Bump the GIL check interval, its more imp parser.error('No inputs specified') if opts.configPrefix is not None: - global gConfigName, gSiteConfigName + global gConfigName, gSiteConfigName, kLocalConfigName gConfigName = '%s.cfg' % opts.configPrefix gSiteConfigName = '%s.site.cfg' % opts.configPrefix + kLocalConfigName = '%s.local.cfg' % opts.configPrefix if opts.numThreads is None: # Python <2.5 has a race condition causing lit to always fail with numThreads>1 @@ -540,10 +545,24 @@ def main(builtinParameters = {}): # Bump the GIL check interval, its more imp # Select and order the tests. numTotalTests = len(tests) + + # First, select based on the filter expression if given. + if opts.filter: + try: + rex = re.compile(opts.filter) + except: + parser.error("invalid regular expression for --filter: %r" % ( + opts.filter)) + tests = [t for t in tests + if rex.search(t.getFullName())] + + # Then select the order. if opts.shuffle: random.shuffle(tests) else: tests.sort(key = lambda t: t.getFullName()) + + # Finally limit the number of tests, if desired. if opts.maxTests is not None: tests = tests[:opts.maxTests] diff --git a/utils/lldbDataFormatters.py b/utils/lldbDataFormatters.py new file mode 100644 index 0000000..18b407a --- /dev/null +++ b/utils/lldbDataFormatters.py @@ -0,0 +1,53 @@ +""" +Load into LLDB with: +script import lldbDataFormatters +type synthetic add -x "^llvm::SmallVectorImpl<.+>$" -l lldbDataFormatters.SmallVectorSynthProvider +""" + +# Pretty printer for llvm::SmallVector/llvm::SmallVectorImpl +class SmallVectorSynthProvider: + def __init__(self, valobj, dict): + self.valobj = valobj; + self.update() # initialize this provider + + def num_children(self): + begin = self.begin.GetValueAsUnsigned(0) + end = self.end.GetValueAsUnsigned(0) + return (end - begin)/self.type_size + + def get_child_index(self, name): + try: + return int(name.lstrip('[').rstrip(']')) + except: + return -1; + + def get_child_at_index(self, index): + # Do bounds checking. + if index < 0: + return None + if index >= self.num_children(): + return None; + + offset = index * self.type_size + return self.begin.CreateChildAtOffset('['+str(index)+']', + offset, self.data_type) + + def get_type_from_name(self): + import re + name = self.valobj.GetType().GetName() + # This class works with both SmallVectors and SmallVectorImpls. + res = re.match("^(llvm::)?SmallVectorImpl<(.+)>$", name) + if res: + return res.group(2) + res = re.match("^(llvm::)?SmallVector<(.+), \d+>$", name) + if res: + return res.group(2) + return None + + def update(self): + self.begin = self.valobj.GetChildMemberWithName('BeginX') + self.end = self.valobj.GetChildMemberWithName('EndX') + data_type = self.get_type_from_name() + # FIXME: this sometimes returns an invalid type. + self.data_type = self.valobj.GetTarget().FindFirstType(data_type) + self.type_size = self.data_type.GetByteSize() diff --git a/utils/llvm-build/README.txt b/utils/llvm-build/README.txt new file mode 100644 index 0000000..b6bcaae --- /dev/null +++ b/utils/llvm-build/README.txt @@ -0,0 +1,5 @@ +============================== + llvm-build - LLVM Build Tool +============================== + +`llvm-build` is a tool for helping build the LLVM project. diff --git a/utils/llvm-build/llvm-build b/utils/llvm-build/llvm-build new file mode 100755 index 0000000..7377e3d --- /dev/null +++ b/utils/llvm-build/llvm-build @@ -0,0 +1,6 @@ +#!/usr/bin/env python + +import llvmbuild + +if __name__ == '__main__': + llvmbuild.main() diff --git a/utils/llvm-build/llvmbuild/__init__.py b/utils/llvm-build/llvmbuild/__init__.py new file mode 100644 index 0000000..7760218 --- /dev/null +++ b/utils/llvm-build/llvmbuild/__init__.py @@ -0,0 +1 @@ +from main import main diff --git a/utils/llvm-build/llvmbuild/componentinfo.py b/utils/llvm-build/llvmbuild/componentinfo.py new file mode 100644 index 0000000..230ae21 --- /dev/null +++ b/utils/llvm-build/llvmbuild/componentinfo.py @@ -0,0 +1,428 @@ +""" +Descriptor objects for entities that are part of the LLVM project. +""" + +import ConfigParser +import StringIO +import sys + +from util import * + +class ParseError(Exception): + pass + +class ComponentInfo(object): + """ + Base class for component descriptions. + """ + + type_name = None + + @staticmethod + def parse_items(items, has_dependencies = True): + kwargs = {} + kwargs['name'] = items.get_string('name') + kwargs['parent'] = items.get_optional_string('parent') + if has_dependencies: + kwargs['dependencies'] = items.get_list('dependencies') + return kwargs + + def __init__(self, subpath, name, dependencies, parent): + if not subpath.startswith('/'): + raise ValueError,"invalid subpath: %r" % subpath + self.subpath = subpath + self.name = name + self.dependencies = list(dependencies) + + # The name of the parent component to logically group this component + # under. + self.parent = parent + + # The parent instance, once loaded. + self.parent_instance = None + self.children = [] + + # The original source path. + self._source_path = None + + # A flag to mark "special" components which have some amount of magic + # handling (generally based on command line options). + self._is_special_group = False + + def set_parent_instance(self, parent): + assert parent.name == self.parent, "Unexpected parent!" + self.parent_instance = parent + self.parent_instance.children.append(self) + + def get_component_references(self): + """get_component_references() -> iter + + Return an iterator over the named references to other components from + this object. Items are of the form (reference-type, component-name). + """ + + # Parent references are handled specially. + for r in self.dependencies: + yield ('dependency', r) + + def get_llvmbuild_fragment(self): + abstract + +class GroupComponentInfo(ComponentInfo): + """ + Group components have no semantics as far as the build system are concerned, + but exist to help organize other components into a logical tree structure. + """ + + type_name = 'Group' + + @staticmethod + def parse(subpath, items): + kwargs = ComponentInfo.parse_items(items, has_dependencies = False) + return GroupComponentInfo(subpath, **kwargs) + + def __init__(self, subpath, name, parent): + ComponentInfo.__init__(self, subpath, name, [], parent) + + def get_llvmbuild_fragment(self): + result = StringIO.StringIO() + print >>result, 'type = %s' % self.type_name + print >>result, 'name = %s' % self.name + print >>result, 'parent = %s' % self.parent + return result.getvalue() + +class LibraryComponentInfo(ComponentInfo): + type_name = 'Library' + + @staticmethod + def parse(subpath, items): + kwargs = ComponentInfo.parse_items(items) + kwargs['library_name'] = items.get_optional_string('library_name') + kwargs['required_libraries'] = items.get_list('required_libraries') + kwargs['add_to_library_groups'] = items.get_list( + 'add_to_library_groups') + return LibraryComponentInfo(subpath, **kwargs) + + def __init__(self, subpath, name, dependencies, parent, library_name, + required_libraries, add_to_library_groups): + ComponentInfo.__init__(self, subpath, name, dependencies, parent) + + # If given, the name to use for the library instead of deriving it from + # the component name. + self.library_name = library_name + + # The names of the library components which are required when linking + # with this component. + self.required_libraries = list(required_libraries) + + # The names of the library group components this component should be + # considered part of. + self.add_to_library_groups = list(add_to_library_groups) + + def get_component_references(self): + for r in ComponentInfo.get_component_references(self): + yield r + for r in self.required_libraries: + yield ('required library', r) + for r in self.add_to_library_groups: + yield ('library group', r) + + def get_llvmbuild_fragment(self): + result = StringIO.StringIO() + print >>result, 'type = %s' % self.type_name + print >>result, 'name = %s' % self.name + print >>result, 'parent = %s' % self.parent + if self.library_name is not None: + print >>result, 'library_name = %s' % self.library_name + if self.required_libraries: + print >>result, 'required_libraries = %s' % ' '.join( + self.required_libraries) + if self.add_to_library_groups: + print >>result, 'add_to_library_groups = %s' % ' '.join( + self.add_to_library_groups) + return result.getvalue() + + def get_library_name(self): + return self.library_name or self.name + + def get_prefixed_library_name(self): + """ + get_prefixed_library_name() -> str + + Return the library name prefixed by the project name. This is generally + what the library name will be on disk. + """ + + basename = self.get_library_name() + + # FIXME: We need to get the prefix information from an explicit project + # object, or something. + if basename in ('gtest', 'gtest_main'): + return basename + + return 'LLVM%s' % basename + + def get_llvmconfig_component_name(self): + return self.get_library_name().lower() + +class LibraryGroupComponentInfo(ComponentInfo): + type_name = 'LibraryGroup' + + @staticmethod + def parse(subpath, items): + kwargs = ComponentInfo.parse_items(items, has_dependencies = False) + kwargs['required_libraries'] = items.get_list('required_libraries') + kwargs['add_to_library_groups'] = items.get_list( + 'add_to_library_groups') + return LibraryGroupComponentInfo(subpath, **kwargs) + + def __init__(self, subpath, name, parent, required_libraries = [], + add_to_library_groups = []): + ComponentInfo.__init__(self, subpath, name, [], parent) + + # The names of the library components which are required when linking + # with this component. + self.required_libraries = list(required_libraries) + + # The names of the library group components this component should be + # considered part of. + self.add_to_library_groups = list(add_to_library_groups) + + def get_component_references(self): + for r in ComponentInfo.get_component_references(self): + yield r + for r in self.required_libraries: + yield ('required library', r) + for r in self.add_to_library_groups: + yield ('library group', r) + + def get_llvmbuild_fragment(self): + result = StringIO.StringIO() + print >>result, 'type = %s' % self.type_name + print >>result, 'name = %s' % self.name + print >>result, 'parent = %s' % self.parent + if self.required_libraries and not self._is_special_group: + print >>result, 'required_libraries = %s' % ' '.join( + self.required_libraries) + if self.add_to_library_groups: + print >>result, 'add_to_library_groups = %s' % ' '.join( + self.add_to_library_groups) + return result.getvalue() + + def get_llvmconfig_component_name(self): + return self.name.lower() + +class TargetGroupComponentInfo(ComponentInfo): + type_name = 'TargetGroup' + + @staticmethod + def parse(subpath, items): + kwargs = ComponentInfo.parse_items(items, has_dependencies = False) + kwargs['required_libraries'] = items.get_list('required_libraries') + kwargs['add_to_library_groups'] = items.get_list( + 'add_to_library_groups') + kwargs['has_jit'] = items.get_optional_bool('has_jit', False) + kwargs['has_asmprinter'] = items.get_optional_bool('has_asmprinter', + False) + kwargs['has_asmparser'] = items.get_optional_bool('has_asmparser', + False) + kwargs['has_disassembler'] = items.get_optional_bool('has_disassembler', + False) + return TargetGroupComponentInfo(subpath, **kwargs) + + def __init__(self, subpath, name, parent, required_libraries = [], + add_to_library_groups = [], has_jit = False, + has_asmprinter = False, has_asmparser = False, + has_disassembler = False): + ComponentInfo.__init__(self, subpath, name, [], parent) + + # The names of the library components which are required when linking + # with this component. + self.required_libraries = list(required_libraries) + + # The names of the library group components this component should be + # considered part of. + self.add_to_library_groups = list(add_to_library_groups) + + # Whether or not this target supports the JIT. + self.has_jit = bool(has_jit) + + # Whether or not this target defines an assembly printer. + self.has_asmprinter = bool(has_asmprinter) + + # Whether or not this target defines an assembly parser. + self.has_asmparser = bool(has_asmparser) + + # Whether or not this target defines an disassembler. + self.has_disassembler = bool(has_disassembler) + + # Whether or not this target is enabled. This is set in response to + # configuration parameters. + self.enabled = False + + def get_component_references(self): + for r in ComponentInfo.get_component_references(self): + yield r + for r in self.required_libraries: + yield ('required library', r) + for r in self.add_to_library_groups: + yield ('library group', r) + + def get_llvmbuild_fragment(self): + result = StringIO.StringIO() + print >>result, 'type = %s' % self.type_name + print >>result, 'name = %s' % self.name + print >>result, 'parent = %s' % self.parent + if self.required_libraries: + print >>result, 'required_libraries = %s' % ' '.join( + self.required_libraries) + if self.add_to_library_groups: + print >>result, 'add_to_library_groups = %s' % ' '.join( + self.add_to_library_groups) + for bool_key in ('has_asmparser', 'has_asmprinter', 'has_disassembler', + 'has_jit'): + if getattr(self, bool_key): + print >>result, '%s = 1' % (bool_key,) + return result.getvalue() + + def get_llvmconfig_component_name(self): + return self.name.lower() + +class ToolComponentInfo(ComponentInfo): + type_name = 'Tool' + + @staticmethod + def parse(subpath, items): + kwargs = ComponentInfo.parse_items(items) + kwargs['required_libraries'] = items.get_list('required_libraries') + return ToolComponentInfo(subpath, **kwargs) + + def __init__(self, subpath, name, dependencies, parent, + required_libraries): + ComponentInfo.__init__(self, subpath, name, dependencies, parent) + + # The names of the library components which are required to link this + # tool. + self.required_libraries = list(required_libraries) + + def get_component_references(self): + for r in ComponentInfo.get_component_references(self): + yield r + for r in self.required_libraries: + yield ('required library', r) + + def get_llvmbuild_fragment(self): + result = StringIO.StringIO() + print >>result, 'type = %s' % self.type_name + print >>result, 'name = %s' % self.name + print >>result, 'parent = %s' % self.parent + print >>result, 'required_libraries = %s' % ' '.join( + self.required_libraries) + return result.getvalue() + +class BuildToolComponentInfo(ToolComponentInfo): + type_name = 'BuildTool' + + @staticmethod + def parse(subpath, items): + kwargs = ComponentInfo.parse_items(items) + kwargs['required_libraries'] = items.get_list('required_libraries') + return BuildToolComponentInfo(subpath, **kwargs) + +### + +class IniFormatParser(dict): + def get_list(self, key): + # Check if the value is defined. + value = self.get(key) + if value is None: + return [] + + # Lists are just whitespace separated strings. + return value.split() + + def get_optional_string(self, key): + value = self.get_list(key) + if not value: + return None + if len(value) > 1: + raise ParseError("multiple values for scalar key: %r" % key) + return value[0] + + def get_string(self, key): + value = self.get_optional_string(key) + if not value: + raise ParseError("missing value for required string: %r" % key) + return value + + def get_optional_bool(self, key, default = None): + value = self.get_optional_string(key) + if not value: + return default + if value not in ('0', '1'): + raise ParseError("invalid value(%r) for boolean property: %r" % ( + value, key)) + return bool(int(value)) + + def get_bool(self, key): + value = self.get_optional_bool(key) + if value is None: + raise ParseError("missing value for required boolean: %r" % key) + return value + +_component_type_map = dict( + (t.type_name, t) + for t in (GroupComponentInfo, + LibraryComponentInfo, LibraryGroupComponentInfo, + ToolComponentInfo, BuildToolComponentInfo, + TargetGroupComponentInfo)) +def load_from_path(path, subpath): + # Load the LLVMBuild.txt file as an .ini format file. + parser = ConfigParser.RawConfigParser() + parser.read(path) + + # Extract the common section. + if parser.has_section("common"): + common = IniFormatParser(parser.items("common")) + parser.remove_section("common") + else: + common = IniFormatParser({}) + + return common, _read_components_from_parser(parser, path, subpath) + +def _read_components_from_parser(parser, path, subpath): + # We load each section which starts with 'component' as a distinct component + # description (so multiple components can be described in one file). + for section in parser.sections(): + if not section.startswith('component'): + # We don't expect arbitrary sections currently, warn the user. + warning("ignoring unknown section %r in %r" % (section, path)) + continue + + # Determine the type of the component to instantiate. + if not parser.has_option(section, 'type'): + fatal("invalid component %r in %r: %s" % ( + section, path, "no component type")) + + type_name = parser.get(section, 'type') + type_class = _component_type_map.get(type_name) + if type_class is None: + fatal("invalid component %r in %r: %s" % ( + section, path, "invalid component type: %r" % type_name)) + + # Instantiate the component based on the remaining values. + try: + info = type_class.parse(subpath, + IniFormatParser(parser.items(section))) + except TypeError: + print >>sys.stderr, "error: invalid component %r in %r: %s" % ( + section, path, "unable to instantiate: %r" % type_name) + import traceback + traceback.print_exc() + raise SystemExit, 1 + except ParseError,e: + fatal("unable to load component %r in %r: %s" % ( + section, path, e.message)) + + info._source_path = path + yield info diff --git a/utils/llvm-build/llvmbuild/configutil.py b/utils/llvm-build/llvmbuild/configutil.py new file mode 100644 index 0000000..b5582c3 --- /dev/null +++ b/utils/llvm-build/llvmbuild/configutil.py @@ -0,0 +1,66 @@ +""" +Defines utilities useful for performing standard "configuration" style tasks. +""" + +import re +import os + +def configure_file(input_path, output_path, substitutions): + """configure_file(input_path, output_path, substitutions) -> bool + + Given an input and output path, "configure" the file at the given input path + by replacing variables in the file with those given in the substitutions + list. Returns true if the output file was written. + + The substitutions list should be given as a list of tuples (regex string, + replacement), where the regex and replacement will be used as in 're.sub' to + execute the variable replacement. + + The output path's parent directory need not exist (it will be created). + + If the output path does exist and the configured data is not different than + it's current contents, the output file will not be modified. This is + designed to limit the impact of configured files on build dependencies. + """ + + # Read in the input data. + f = open(input_path, "rb") + try: + data = f.read() + finally: + f.close() + + # Perform the substitutions. + for regex_string,replacement in substitutions: + regex = re.compile(regex_string) + data = regex.sub(replacement, data) + + # Ensure the output parent directory exists. + output_parent_path = os.path.dirname(os.path.abspath(output_path)) + if not os.path.exists(output_parent_path): + os.makedirs(output_parent_path) + + # If the output path exists, load it and compare to the configured contents. + if os.path.exists(output_path): + current_data = None + try: + f = open(output_path, "rb") + try: + current_data = f.read() + except: + current_data = None + f.close() + except: + current_data = None + + if current_data is not None and current_data == data: + return False + + # Write the output contents. + f = open(output_path, "wb") + try: + f.write(data) + finally: + f.close() + + return True diff --git a/utils/llvm-build/llvmbuild/main.py b/utils/llvm-build/llvmbuild/main.py new file mode 100644 index 0000000..36bca87 --- /dev/null +++ b/utils/llvm-build/llvmbuild/main.py @@ -0,0 +1,868 @@ +import StringIO +import os +import sys + +import componentinfo +import configutil + +from util import * + +### + +def cmake_quote_string(value): + """ + cmake_quote_string(value) -> str + + Return a quoted form of the given value that is suitable for use in CMake + language files. + """ + + # Currently, we only handle escaping backslashes. + value = value.replace("\\", "\\\\") + + return value + +def cmake_quote_path(value): + """ + cmake_quote_path(value) -> str + + Return a quoted form of the given value that is suitable for use in CMake + language files. + """ + + # CMake has a bug in it's Makefile generator that doesn't properly quote + # strings it generates. So instead of using proper quoting, we just use "/" + # style paths. Currently, we only handle escaping backslashes. + value = value.replace("\\", "/") + + return value + +def mk_quote_string_for_target(value): + """ + mk_quote_string_for_target(target_name) -> str + + Return a quoted form of the given target_name suitable for including in a + Makefile as a target name. + """ + + # The only quoting we currently perform is for ':', to support msys users. + return value.replace(":", "\\:") + +def make_install_dir(path): + """ + make_install_dir(path) -> None + + Create the given directory path for installation, including any parents. + """ + + # os.makedirs considers it an error to be called with an existant path. + if not os.path.exists(path): + os.makedirs(path) + +### + +class LLVMProjectInfo(object): + @staticmethod + def load_infos_from_path(llvmbuild_source_root): + def recurse(subpath): + # Load the LLVMBuild file. + llvmbuild_path = os.path.join(llvmbuild_source_root + subpath, + 'LLVMBuild.txt') + if not os.path.exists(llvmbuild_path): + fatal("missing LLVMBuild.txt file at: %r" % (llvmbuild_path,)) + + # Parse the components from it. + common,info_iter = componentinfo.load_from_path(llvmbuild_path, + subpath) + for info in info_iter: + yield info + + # Recurse into the specified subdirectories. + for subdir in common.get_list("subdirectories"): + for item in recurse(os.path.join(subpath, subdir)): + yield item + + return recurse("/") + + @staticmethod + def load_from_path(source_root, llvmbuild_source_root): + infos = list( + LLVMProjectInfo.load_infos_from_path(llvmbuild_source_root)) + + return LLVMProjectInfo(source_root, infos) + + def __init__(self, source_root, component_infos): + # Store our simple ivars. + self.source_root = source_root + self.component_infos = list(component_infos) + self.component_info_map = None + self.ordered_component_infos = None + + def validate_components(self): + """validate_components() -> None + + Validate that the project components are well-defined. Among other + things, this checks that: + - Components have valid references. + - Components references do not form cycles. + + We also construct the map from component names to info, and the + topological ordering of components. + """ + + # Create the component info map and validate that component names are + # unique. + self.component_info_map = {} + for ci in self.component_infos: + existing = self.component_info_map.get(ci.name) + if existing is not None: + # We found a duplicate component name, report it and error out. + fatal("found duplicate component %r (at %r and %r)" % ( + ci.name, ci.subpath, existing.subpath)) + self.component_info_map[ci.name] = ci + + # Disallow 'all' as a component name, which is a special case. + if 'all' in self.component_info_map: + fatal("project is not allowed to define 'all' component") + + # Add the root component. + if '$ROOT' in self.component_info_map: + fatal("project is not allowed to define $ROOT component") + self.component_info_map['$ROOT'] = componentinfo.GroupComponentInfo( + '/', '$ROOT', None) + self.component_infos.append(self.component_info_map['$ROOT']) + + # Topologically order the component information according to their + # component references. + def visit_component_info(ci, current_stack, current_set): + # Check for a cycles. + if ci in current_set: + # We found a cycle, report it and error out. + cycle_description = ' -> '.join( + '%r (%s)' % (ci.name, relation) + for relation,ci in current_stack) + fatal("found cycle to %r after following: %s -> %s" % ( + ci.name, cycle_description, ci.name)) + + # If we have already visited this item, we are done. + if ci not in components_to_visit: + return + + # Otherwise, mark the component info as visited and traverse. + components_to_visit.remove(ci) + + # Validate the parent reference, which we treat specially. + if ci.parent is not None: + parent = self.component_info_map.get(ci.parent) + if parent is None: + fatal("component %r has invalid reference %r (via %r)" % ( + ci.name, ci.parent, 'parent')) + ci.set_parent_instance(parent) + + for relation,referent_name in ci.get_component_references(): + # Validate that the reference is ok. + referent = self.component_info_map.get(referent_name) + if referent is None: + fatal("component %r has invalid reference %r (via %r)" % ( + ci.name, referent_name, relation)) + + # Visit the reference. + current_stack.append((relation,ci)) + current_set.add(ci) + visit_component_info(referent, current_stack, current_set) + current_set.remove(ci) + current_stack.pop() + + # Finally, add the component info to the ordered list. + self.ordered_component_infos.append(ci) + + # FIXME: We aren't actually correctly checking for cycles along the + # parent edges. Haven't decided how I want to handle this -- I thought + # about only checking cycles by relation type. If we do that, it falls + # out easily. If we don't, we should special case the check. + + self.ordered_component_infos = [] + components_to_visit = set(self.component_infos) + while components_to_visit: + visit_component_info(iter(components_to_visit).next(), [], set()) + + # Canonicalize children lists. + for c in self.ordered_component_infos: + c.children.sort(key = lambda c: c.name) + + def print_tree(self): + def visit(node, depth = 0): + print '%s%-40s (%s)' % (' '*depth, node.name, node.type_name) + for c in node.children: + visit(c, depth + 1) + visit(self.component_info_map['$ROOT']) + + def write_components(self, output_path): + # Organize all the components by the directory their LLVMBuild file + # should go in. + info_basedir = {} + for ci in self.component_infos: + # Ignore the $ROOT component. + if ci.parent is None: + continue + + info_basedir[ci.subpath] = info_basedir.get(ci.subpath, []) + [ci] + + # Compute the list of subdirectories to scan. + subpath_subdirs = {} + for ci in self.component_infos: + # Ignore root components. + if ci.subpath == '/': + continue + + # Otherwise, append this subpath to the parent list. + parent_path = os.path.dirname(ci.subpath) + subpath_subdirs[parent_path] = parent_list = subpath_subdirs.get( + parent_path, set()) + parent_list.add(os.path.basename(ci.subpath)) + + # Generate the build files. + for subpath, infos in info_basedir.items(): + # Order the components by name to have a canonical ordering. + infos.sort(key = lambda ci: ci.name) + + # Format the components into llvmbuild fragments. + fragments = [] + + # Add the common fragments. + subdirectories = subpath_subdirs.get(subpath) + if subdirectories: + fragment = """\ +subdirectories = %s +""" % (" ".join(sorted(subdirectories)),) + fragments.append(("common", fragment)) + + # Add the component fragments. + num_common_fragments = len(fragments) + for ci in infos: + fragment = ci.get_llvmbuild_fragment() + if fragment is None: + continue + + name = "component_%d" % (len(fragments) - num_common_fragments) + fragments.append((name, fragment)) + + if not fragments: + continue + + assert subpath.startswith('/') + directory_path = os.path.join(output_path, subpath[1:]) + + # Create the directory if it does not already exist. + if not os.path.exists(directory_path): + os.makedirs(directory_path) + + # In an effort to preserve comments (which aren't parsed), read in + # the original file and extract the comments. We only know how to + # associate comments that prefix a section name. + f = open(infos[0]._source_path) + comments_map = {} + comment_block = "" + for ln in f: + if ln.startswith(';'): + comment_block += ln + elif ln.startswith('[') and ln.endswith(']\n'): + comments_map[ln[1:-2]] = comment_block + else: + comment_block = "" + f.close() + + # Create the LLVMBuild fil[e. + file_path = os.path.join(directory_path, 'LLVMBuild.txt') + f = open(file_path, "w") + + # Write the header. + header_fmt = ';===- %s %s-*- Conf -*--===;' + header_name = '.' + os.path.join(subpath, 'LLVMBuild.txt') + header_pad = '-' * (80 - len(header_fmt % (header_name, ''))) + header_string = header_fmt % (header_name, header_pad) + print >>f, """\ +%s +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; +""" % header_string + + # Write out each fragment.each component fragment. + for name,fragment in fragments: + comment = comments_map.get(name) + if comment is not None: + f.write(comment) + print >>f, "[%s]" % name + f.write(fragment) + if fragment is not fragments[-1][1]: + print >>f + + f.close() + + def write_library_table(self, output_path): + # Write out the mapping from component names to required libraries. + # + # We do this in topological order so that we know we can append the + # dependencies for added library groups. + entries = {} + for c in self.ordered_component_infos: + # Only certain components are in the table. + if c.type_name not in ('Library', 'LibraryGroup', 'TargetGroup'): + continue + + # Compute the llvm-config "component name". For historical reasons, + # this is lowercased based on the library name. + llvmconfig_component_name = c.get_llvmconfig_component_name() + + # Get the library name, or None for LibraryGroups. + if c.type_name == 'Library': + library_name = c.get_prefixed_library_name() + else: + library_name = None + + # Get the component names of all the required libraries. + required_llvmconfig_component_names = [ + self.component_info_map[dep].get_llvmconfig_component_name() + for dep in c.required_libraries] + + # Insert the entries for library groups we should add to. + for dep in c.add_to_library_groups: + entries[dep][2].append(llvmconfig_component_name) + + # Add the entry. + entries[c.name] = (llvmconfig_component_name, library_name, + required_llvmconfig_component_names) + + # Convert to a list of entries and sort by name. + entries = entries.values() + + # Create an 'all' pseudo component. We keep the dependency list small by + # only listing entries that have no other dependents. + root_entries = set(e[0] for e in entries) + for _,_,deps in entries: + root_entries -= set(deps) + entries.append(('all', None, root_entries)) + + entries.sort() + + # Compute the maximum number of required libraries, plus one so there is + # always a sentinel. + max_required_libraries = max(len(deps) + for _,_,deps in entries) + 1 + + # Write out the library table. + make_install_dir(os.path.dirname(output_path)) + f = open(output_path, 'w') + print >>f, """\ +//===- llvm-build generated file --------------------------------*- C++ -*-===// +// +// Component Library Depenedency Table +// +// Automatically generated file, do not edit! +// +//===----------------------------------------------------------------------===// +""" + print >>f, 'struct AvailableComponent {' + print >>f, ' /// The name of the component.' + print >>f, ' const char *Name;' + print >>f, '' + print >>f, ' /// The name of the library for this component (or NULL).' + print >>f, ' const char *Library;' + print >>f, '' + print >>f, '\ + /// The list of libraries required when linking this component.' + print >>f, ' const char *RequiredLibraries[%d];' % ( + max_required_libraries) + print >>f, '} AvailableComponents[%d] = {' % len(entries) + for name,library_name,required_names in entries: + if library_name is None: + library_name_as_cstr = '0' + else: + library_name_as_cstr = '"lib%s.a"' % library_name + print >>f, ' { "%s", %s, { %s } },' % ( + name, library_name_as_cstr, + ', '.join('"%s"' % dep + for dep in required_names)) + print >>f, '};' + f.close() + + def get_required_libraries_for_component(self, ci, traverse_groups = False): + """ + get_required_libraries_for_component(component_info) -> iter + + Given a Library component info descriptor, return an iterator over all + of the directly required libraries for linking with this component. If + traverse_groups is True, then library and target groups will be + traversed to include their required libraries. + """ + + assert ci.type_name in ('Library', 'LibraryGroup', 'TargetGroup') + + for name in ci.required_libraries: + # Get the dependency info. + dep = self.component_info_map[name] + + # If it is a library, yield it. + if dep.type_name == 'Library': + yield dep + continue + + # Otherwise if it is a group, yield or traverse depending on what + # was requested. + if dep.type_name in ('LibraryGroup', 'TargetGroup'): + if not traverse_groups: + yield dep + continue + + for res in self.get_required_libraries_for_component(dep, True): + yield res + + def get_fragment_dependencies(self): + """ + get_fragment_dependencies() -> iter + + Compute the list of files (as absolute paths) on which the output + fragments depend (i.e., files for which a modification should trigger a + rebuild of the fragment). + """ + + # Construct a list of all the dependencies of the Makefile fragment + # itself. These include all the LLVMBuild files themselves, as well as + # all of our own sources. + # + # Many components may come from the same file, so we make sure to unique + # these. + build_paths = set() + for ci in self.component_infos: + p = os.path.join(self.source_root, ci.subpath[1:], 'LLVMBuild.txt') + if p not in build_paths: + yield p + build_paths.add(p) + + # Gather the list of necessary sources by just finding all loaded + # modules that are inside the LLVM source tree. + for module in sys.modules.values(): + # Find the module path. + if not hasattr(module, '__file__'): + continue + path = getattr(module, '__file__') + if not path: + continue + + # Strip off any compiled suffix. + if os.path.splitext(path)[1] in ['.pyc', '.pyo', '.pyd']: + path = path[:-1] + + # If the path exists and is in the source tree, consider it a + # dependency. + if (path.startswith(self.source_root) and os.path.exists(path)): + yield path + + def write_cmake_fragment(self, output_path): + """ + write_cmake_fragment(output_path) -> None + + Generate a CMake fragment which includes all of the collated LLVMBuild + information in a format that is easily digestible by a CMake. The exact + contents of this are closely tied to how the CMake configuration + integrates LLVMBuild, see CMakeLists.txt in the top-level. + """ + + dependencies = list(self.get_fragment_dependencies()) + + # Write out the CMake fragment. + make_install_dir(os.path.dirname(output_path)) + f = open(output_path, 'w') + + # Write the header. + header_fmt = '\ +#===-- %s - LLVMBuild Configuration for LLVM %s-*- CMake -*--===#' + header_name = os.path.basename(output_path) + header_pad = '-' * (80 - len(header_fmt % (header_name, ''))) + header_string = header_fmt % (header_name, header_pad) + print >>f, """\ +%s +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# +# +# This file contains the LLVMBuild project information in a format easily +# consumed by the CMake based build system. +# +# This file is autogenerated by llvm-build, do not edit! +# +#===------------------------------------------------------------------------===# +""" % header_string + + # Write the dependency information in the best way we can. + print >>f, """ +# LLVMBuild CMake fragment dependencies. +# +# CMake has no builtin way to declare that the configuration depends on +# a particular file. However, a side effect of configure_file is to add +# said input file to CMake's internal dependency list. So, we use that +# and a dummy output file to communicate the dependency information to +# CMake. +# +# FIXME: File a CMake RFE to get a properly supported version of this +# feature.""" + for dep in dependencies: + print >>f, """\ +configure_file(\"%s\" + ${CMAKE_CURRENT_BINARY_DIR}/DummyConfigureOutput)""" % ( + cmake_quote_path(dep),) + + # Write the properties we use to encode the required library dependency + # information in a form CMake can easily use directly. + print >>f, """ +# Explicit library dependency information. +# +# The following property assignments effectively create a map from component +# names to required libraries, in a way that is easily accessed from CMake.""" + for ci in self.ordered_component_infos: + # We only write the information for libraries currently. + if ci.type_name != 'Library': + continue + + print >>f, """\ +set_property(GLOBAL PROPERTY LLVMBUILD_LIB_DEPS_%s %s)""" % ( + ci.get_prefixed_library_name(), " ".join(sorted( + dep.get_prefixed_library_name() + for dep in self.get_required_libraries_for_component(ci)))) + + f.close() + + def write_make_fragment(self, output_path): + """ + write_make_fragment(output_path) -> None + + Generate a Makefile fragment which includes all of the collated + LLVMBuild information in a format that is easily digestible by a + Makefile. The exact contents of this are closely tied to how the LLVM + Makefiles integrate LLVMBuild, see Makefile.rules in the top-level. + """ + + dependencies = list(self.get_fragment_dependencies()) + + # Write out the Makefile fragment. + make_install_dir(os.path.dirname(output_path)) + f = open(output_path, 'w') + + # Write the header. + header_fmt = '\ +#===-- %s - LLVMBuild Configuration for LLVM %s-*- Makefile -*--===#' + header_name = os.path.basename(output_path) + header_pad = '-' * (80 - len(header_fmt % (header_name, ''))) + header_string = header_fmt % (header_name, header_pad) + print >>f, """\ +%s +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# +# +# This file contains the LLVMBuild project information in a format easily +# consumed by the Makefile based build system. +# +# This file is autogenerated by llvm-build, do not edit! +# +#===------------------------------------------------------------------------===# +""" % header_string + + # Write the dependencies for the fragment. + # + # FIXME: Technically, we need to properly quote for Make here. + print >>f, """\ +# Clients must explicitly enable LLVMBUILD_INCLUDE_DEPENDENCIES to get +# these dependencies. This is a compromise to help improve the +# performance of recursive Make systems.""" + print >>f, 'ifeq ($(LLVMBUILD_INCLUDE_DEPENDENCIES),1)' + print >>f, "# The dependencies for this Makefile fragment itself." + print >>f, "%s: \\" % (mk_quote_string_for_target(output_path),) + for dep in dependencies: + print >>f, "\t%s \\" % (dep,) + print >>f + + # Generate dummy rules for each of the dependencies, so that things + # continue to work correctly if any of those files are moved or removed. + print >>f, """\ +# The dummy targets to allow proper regeneration even when files are moved or +# removed.""" + for dep in dependencies: + print >>f, "%s:" % (mk_quote_string_for_target(dep),) + print >>f, 'endif' + + f.close() + +def add_magic_target_components(parser, project, opts): + """add_magic_target_components(project, opts) -> None + + Add the "magic" target based components to the project, which can only be + determined based on the target configuration options. + + This currently is responsible for populating the required_libraries list of + the "all-targets", "Native", "NativeCodeGen", and "Engine" components. + """ + + # Determine the available targets. + available_targets = dict((ci.name,ci) + for ci in project.component_infos + if ci.type_name == 'TargetGroup') + + # Find the configured native target. + + # We handle a few special cases of target names here for historical + # reasons, as these are the names configure currently comes up with. + native_target_name = { 'x86' : 'X86', + 'x86_64' : 'X86', + 'Unknown' : None }.get(opts.native_target, + opts.native_target) + if native_target_name is None: + native_target = None + else: + native_target = available_targets.get(native_target_name) + if native_target is None: + parser.error("invalid native target: %r (not in project)" % ( + opts.native_target,)) + if native_target.type_name != 'TargetGroup': + parser.error("invalid native target: %r (not a target)" % ( + opts.native_target,)) + + # Find the list of targets to enable. + if opts.enable_targets is None: + enable_targets = available_targets.values() + else: + # We support both space separated and semi-colon separated lists. + if ' ' in opts.enable_targets: + enable_target_names = opts.enable_targets.split() + else: + enable_target_names = opts.enable_targets.split(';') + + enable_targets = [] + for name in enable_target_names: + target = available_targets.get(name) + if target is None: + parser.error("invalid target to enable: %r (not in project)" % ( + name,)) + if target.type_name != 'TargetGroup': + parser.error("invalid target to enable: %r (not a target)" % ( + name,)) + enable_targets.append(target) + + # Find the special library groups we are going to populate. We enforce that + # these appear in the project (instead of just adding them) so that they at + # least have an explicit representation in the project LLVMBuild files (and + # comments explaining how they are populated). + def find_special_group(name): + info = info_map.get(name) + if info is None: + fatal("expected project to contain special %r component" % ( + name,)) + + if info.type_name != 'LibraryGroup': + fatal("special component %r should be a LibraryGroup" % ( + name,)) + + if info.required_libraries: + fatal("special component %r must have empty %r list" % ( + name, 'required_libraries')) + if info.add_to_library_groups: + fatal("special component %r must have empty %r list" % ( + name, 'add_to_library_groups')) + + info._is_special_group = True + return info + + info_map = dict((ci.name, ci) for ci in project.component_infos) + all_targets = find_special_group('all-targets') + native_group = find_special_group('Native') + native_codegen_group = find_special_group('NativeCodeGen') + engine_group = find_special_group('Engine') + + # Set the enabled bit in all the target groups, and append to the + # all-targets list. + for ci in enable_targets: + all_targets.required_libraries.append(ci.name) + ci.enabled = True + + # If we have a native target, then that defines the native and + # native_codegen libraries. + if native_target and native_target.enabled: + native_group.required_libraries.append(native_target.name) + native_codegen_group.required_libraries.append( + '%sCodeGen' % native_target.name) + + # If we have a native target with a JIT, use that for the engine. Otherwise, + # use the interpreter. + if native_target and native_target.enabled and native_target.has_jit: + engine_group.required_libraries.append('JIT') + engine_group.required_libraries.append(native_group.name) + else: + engine_group.required_libraries.append('Interpreter') + +def main(): + from optparse import OptionParser, OptionGroup + parser = OptionParser("usage: %prog [options]") + + group = OptionGroup(parser, "Input Options") + group.add_option("", "--source-root", dest="source_root", metavar="PATH", + help="Path to the LLVM source (inferred if not given)", + action="store", default=None) + group.add_option("", "--llvmbuild-source-root", + dest="llvmbuild_source_root", + help=( + "If given, an alternate path to search for LLVMBuild.txt files"), + action="store", default=None, metavar="PATH") + group.add_option("", "--build-root", dest="build_root", metavar="PATH", + help="Path to the build directory (if needed) [%default]", + action="store", default=None) + parser.add_option_group(group) + + group = OptionGroup(parser, "Output Options") + group.add_option("", "--print-tree", dest="print_tree", + help="Print out the project component tree [%default]", + action="store_true", default=False) + group.add_option("", "--write-llvmbuild", dest="write_llvmbuild", + help="Write out the LLVMBuild.txt files to PATH", + action="store", default=None, metavar="PATH") + group.add_option("", "--write-library-table", + dest="write_library_table", metavar="PATH", + help="Write the C++ library dependency table to PATH", + action="store", default=None) + group.add_option("", "--write-cmake-fragment", + dest="write_cmake_fragment", metavar="PATH", + help="Write the CMake project information to PATH", + action="store", default=None) + group.add_option("", "--write-make-fragment", + dest="write_make_fragment", metavar="PATH", + help="Write the Makefile project information to PATH", + action="store", default=None) + group.add_option("", "--configure-target-def-file", + dest="configure_target_def_files", + help="""Configure the given file at SUBPATH (relative to +the inferred or given source root, and with a '.in' suffix) by replacing certain +substitution variables with lists of targets that support certain features (for +example, targets with AsmPrinters) and write the result to the build root (as +given by --build-root) at the same SUBPATH""", + metavar="SUBPATH", action="append", default=None) + parser.add_option_group(group) + + group = OptionGroup(parser, "Configuration Options") + group.add_option("", "--native-target", + dest="native_target", metavar="NAME", + help=("Treat the named target as the 'native' one, if " + "given [%default]"), + action="store", default=None) + group.add_option("", "--enable-targets", + dest="enable_targets", metavar="NAMES", + help=("Enable the given space or semi-colon separated " + "list of targets, or all targets if not present"), + action="store", default=None) + parser.add_option_group(group) + + (opts, args) = parser.parse_args() + + # Determine the LLVM source path, if not given. + source_root = opts.source_root + if source_root: + if not os.path.exists(os.path.join(source_root, 'lib', 'VMCore', + 'Function.cpp')): + parser.error('invalid LLVM source root: %r' % source_root) + else: + llvmbuild_path = os.path.dirname(__file__) + llvm_build_path = os.path.dirname(llvmbuild_path) + utils_path = os.path.dirname(llvm_build_path) + source_root = os.path.dirname(utils_path) + if not os.path.exists(os.path.join(source_root, 'lib', 'VMCore', + 'Function.cpp')): + parser.error('unable to infer LLVM source root, please specify') + + # Construct the LLVM project information. + llvmbuild_source_root = opts.llvmbuild_source_root or source_root + project_info = LLVMProjectInfo.load_from_path( + source_root, llvmbuild_source_root) + + # Add the magic target based components. + add_magic_target_components(parser, project_info, opts) + + # Validate the project component info. + project_info.validate_components() + + # Print the component tree, if requested. + if opts.print_tree: + project_info.print_tree() + + # Write out the components, if requested. This is useful for auto-upgrading + # the schema. + if opts.write_llvmbuild: + project_info.write_components(opts.write_llvmbuild) + + # Write out the required library table, if requested. + if opts.write_library_table: + project_info.write_library_table(opts.write_library_table) + + # Write out the make fragment, if requested. + if opts.write_make_fragment: + project_info.write_make_fragment(opts.write_make_fragment) + + # Write out the cmake fragment, if requested. + if opts.write_cmake_fragment: + project_info.write_cmake_fragment(opts.write_cmake_fragment) + + # Configure target definition files, if requested. + if opts.configure_target_def_files: + # Verify we were given a build root. + if not opts.build_root: + parser.error("must specify --build-root when using " + "--configure-target-def-file") + + # Create the substitution list. + available_targets = [ci for ci in project_info.component_infos + if ci.type_name == 'TargetGroup'] + substitutions = [ + ("@LLVM_ENUM_TARGETS@", + ' '.join('LLVM_TARGET(%s)' % ci.name + for ci in available_targets)), + ("@LLVM_ENUM_ASM_PRINTERS@", + ' '.join('LLVM_ASM_PRINTER(%s)' % ci.name + for ci in available_targets + if ci.has_asmprinter)), + ("@LLVM_ENUM_ASM_PARSERS@", + ' '.join('LLVM_ASM_PARSER(%s)' % ci.name + for ci in available_targets + if ci.has_asmparser)), + ("@LLVM_ENUM_DISASSEMBLERS@", + ' '.join('LLVM_DISASSEMBLER(%s)' % ci.name + for ci in available_targets + if ci.has_disassembler))] + + # Configure the given files. + for subpath in opts.configure_target_def_files: + inpath = os.path.join(source_root, subpath + '.in') + outpath = os.path.join(opts.build_root, subpath) + result = configutil.configure_file(inpath, outpath, substitutions) + if not result: + note("configured file %r hasn't changed" % outpath) + +if __name__=='__main__': + main() diff --git a/utils/llvm-build/llvmbuild/util.py b/utils/llvm-build/llvmbuild/util.py new file mode 100644 index 0000000..e581af2 --- /dev/null +++ b/utils/llvm-build/llvmbuild/util.py @@ -0,0 +1,13 @@ +import os +import sys + +def _write_message(kind, message): + program = os.path.basename(sys.argv[0]) + print >>sys.stderr, '%s: %s: %s' % (program, kind, message) + +note = lambda message: _write_message('note', message) +warning = lambda message: _write_message('warning', message) +error = lambda message: _write_message('error', message) +fatal = lambda message: (_write_message('fatal error', message), sys.exit(1)) + +__all__ = ['note', 'warning', 'error', 'fatal'] diff --git a/utils/llvmbuild b/utils/llvm-compilers-check index b623d32..623ebc6 100755 --- a/utils/llvmbuild +++ b/utils/llvm-compilers-check @@ -9,66 +9,27 @@ ##===----------------------------------------------------------------------===## # # This script builds many different flavors of the LLVM ecosystem. It -# will build LLVM, Clang, llvm-gcc, and dragonegg as well as run tests -# on them. This script is convenient to use to check builds and tests -# before committing changes to the upstream repository +# will build LLVM, Clang and dragonegg as well as run tests on them. +# This script is convenient to use to check builds and tests before +# committing changes to the upstream repository # # A typical source setup uses three trees and looks like this: # # official # dragonegg -# trunk -# gcc -# trunk # llvm -# trunk -# tools -# clang -# tags -# RELEASE_28 -# tools -# clang -# llvm-gcc -# trunk -# tags -# RELEASE_28 +# tools +# clang # staging # dragonegg -# trunk -# gcc -# trunk # llvm -# trunk -# tools -# clang -# tags -# RELEASE_28 -# tools -# clang -# llvm-gcc -# trunk -# tags -# RELEASE_28 +# tools +# clang # commit # dragonegg -# trunk -# gcc -# trunk # llvm -# trunk -# tools -# clang -# tags -# RELEASE_28 -# tools -# clang -# llvm-gcc -# trunk -# tags -# RELEASE_28 -# -# "gcc" above is the upstream FSF gcc and "gcc/trunk" refers to the -# 4.5 branch as discussed in the dragonegg build guide. +# tools +# clang # # In a typical workflow, the "official" tree always contains unchanged # sources from the main LLVM project repositories. The "staging" tree @@ -82,30 +43,29 @@ # # A build may be invoked as such: # -# llvmbuild --src=~/llvm/commit --src=~/llvm/staging -# --src=~/llvm/official --branch=trunk --branch=tags/RELEASE_28 +# llvmbuild --src=~/llvm/commit --src=~/llvm/staging --src=~/llvm/official # --build=debug --build=release --build=paranoid # --prefix=/home/greened/install --builddir=/home/greened/build # -# This will build the LLVM ecosystem, including LLVM, Clang, llvm-gcc, -# gcc 4.5 and dragonegg, putting build results in ~/build and -# installing tools in ~/install. llvmbuild creates separate build and -# install directories for each source/branch/build flavor. In the -# above example, llvmbuild will build debug, release and paranoid -# (debug+checks) flavors of the trunk and RELEASE_28 branches from -# each source tree (official, staging and commit) for a total of -# eighteen builds. All builds will be run in parallel. +# This will build the LLVM ecosystem, including LLVM, Clangand +# dragonegg, putting build results in ~/build and installing tools in +# ~/install. llvm-compilers-check creates separate build and install +# directories for each source/build flavor. In the above example, +# llvmbuild will build debug, release and paranoid (debug+checks) +# flavors from each source tree (official, staging and commit) for a +# total of nine builds. All builds will be run in parallel. # # The user may control parallelism via the --jobs and --threads -# switches. --jobs tells llvmbuild the maximum total number of builds -# to activate in parallel. The user may think of it as equivalent to -# the GNU make -j switch. --threads tells llvmbuild how many worker -# threads to use to accomplish those builds. If --threads is less -# than --jobs, --threads workers will be launched and each one will -# pick a source/branch/flavor combination to build. Then llvmbuild -# will invoke GNU make with -j (--jobs / --threads) to use up the -# remaining job capacity. Once a worker is finished with a build, it -# will pick another combination off the list and start building it. +# switches. --jobs tells llvm-compilers-checl the maximum total +# number of builds to activate in parallel. The user may think of it +# as equivalent to the GNU make -j switch. --threads tells +# llvm-compilers-check how many worker threads to use to accomplish +# those builds. If --threads is less than --jobs, --threads workers +# will be launched and each one will pick a source/flavor combination +# to build. Then llvm-compilers-check will invoke GNU make with -j +# (--jobs / --threads) to use up the remaining job capacity. Once a +# worker is finished with a build, it will pick another combination +# off the list and start building it. # ##===----------------------------------------------------------------------===## @@ -165,8 +125,6 @@ def add_options(parser): help=("Top-level source directory [default: %default]")) parser.add_option("--build", action="append", help=("Build types to run [default: %default]")) - parser.add_option("--branch", action="append", - help=("Source branch to build [default: %default]")) parser.add_option("--cc", default=find_executable("cc"), help=("The C compiler to use [default: %default]")) parser.add_option("--cxx", default=find_executable("c++"), @@ -183,14 +141,10 @@ def add_options(parser): help=("Root build directory [default: %default]")) parser.add_option("--extra-llvm-config-flags", default="", help=("Extra flags to pass to llvm configure [default: %default]")) - parser.add_option("--extra-llvm-gcc-config-flags", default="", - help=("Extra flags to pass to llvm-gcc configure [default: %default]")) - parser.add_option("--extra-gcc-config-flags", default="", - help=("Extra flags to pass to gcc configure [default: %default]")) parser.add_option("--force-configure", default=False, action="store_true", help=("Force reconfigure of all components")) - parser.add_option("--no-gcc", default=False, action="store_true", - help=("Do not build dragonegg and gcc")) + parser.add_option("--no-dragonegg", default=False, action="store_true", + help=("Do not build dragonegg")) parser.add_option("--no-install", default=False, action="store_true", help=("Do not do installs")) return @@ -209,11 +163,6 @@ def check_options(parser, options, valid_builds): compsrc = src + "/" + component if (not os.path.isdir(compsrc)): parser.error("'" + compsrc + "' does not exist") - if (options.branch is not None): - for branch in options.branch: - if (not os.path.isdir(os.path.join(compsrc, branch))): - parser.error("'" + os.path.join(compsrc, branch) - + "' does not exist") # See if we can find the compilers options.cc = find_executable(options.cc) @@ -298,7 +247,7 @@ class Builder(threading.Thread): class FileNotExecutable(Exception): pass def __init__(self, work_queue, jobs, - build_abbrev, source_abbrev, branch_abbrev, + build_abbrev, source_abbrev, options): super().__init__() self.work_queue = work_queue @@ -307,21 +256,17 @@ class Builder(threading.Thread): self.cxx = options.cxx self.build_abbrev = build_abbrev self.source_abbrev = source_abbrev - self.branch_abbrev = branch_abbrev self.build_prefix = options.builddir self.install_prefix = options.prefix self.options = options self.component_abbrev = dict( llvm="llvm", - llvm_gcc="lgcc", - llvm2="llv2", - gcc="ugcc", - dagonegg="degg") + dragonegg="degg") def run(self): while True: try: - source, branch, build = self.work_queue.get() - self.dobuild(source, branch, build) + source, build = self.work_queue.get() + self.dobuild(source, build) except: traceback.print_exc() finally: @@ -387,21 +332,14 @@ class Builder(threading.Thread): self.logger.debug(includes) return includes - def dobuild(self, source, branch, build): + def dobuild(self, source, build): build_suffix = "" ssabbrev = get_short_abbrevs([ab for ab in self.source_abbrev.values()]) - if branch is not None: - sbabbrev = get_short_abbrevs([ab for ab in self.branch_abbrev.values()]) - - prefix = "[" + ssabbrev[self.source_abbrev[source]] + "-" + sbabbrev[self.branch_abbrev[branch]] + "-" + self.build_abbrev[build] + "]" - self.install_prefix += "/" + self.source_abbrev[source] + "/" + branch + "/" + build - build_suffix += self.source_abbrev[source] + "/" + branch + "/" + build - else: - prefix = "[" + ssabbrev[self.source_abbrev[source]] + "-" + self.build_abbrev[build] + "]" - self.install_prefix += "/" + self.source_abbrev[source] + "/" + build - build_suffix += "/" + self.source_abbrev[source] + "/" + build + prefix = "[" + ssabbrev[self.source_abbrev[source]] + "-" + self.build_abbrev[build] + "]" + self.install_prefix += "/" + self.source_abbrev[source] + "/" + build + build_suffix += "/" + self.source_abbrev[source] + "/" + build self.logger = logging.getLogger(prefix) @@ -409,74 +347,26 @@ class Builder(threading.Thread): # Assume we're building with gcc for now. cxxincludes = self.get_includes() - cxxroot = cxxincludes[0] - cxxarch = os.path.basename(cxxincludes[1]) + cxxroot = os.path.dirname(cxxincludes[0]) # Remove the version + cxxroot = os.path.dirname(cxxroot) # Remove the c++ + cxxroot = os.path.dirname(cxxroot) # Remove the include configure_flags = dict( llvm=dict(debug=["--prefix=" + self.install_prefix, "--with-extra-options=-Werror", "--enable-assertions", "--disable-optimized", - "--with-cxx-include-root=" + cxxroot, - "--with-cxx-include-arch=" + cxxarch], + "--with-gcc-toolchain=" + cxxroot], release=["--prefix=" + self.install_prefix, "--with-extra-options=-Werror", "--enable-optimized", - "--with-cxx-include-root=" + cxxroot, - "--with-cxx-include-arch=" + cxxarch], + "--with-gcc-toolchain=" + cxxroot], paranoid=["--prefix=" + self.install_prefix, "--with-extra-options=-Werror", "--enable-assertions", "--enable-expensive-checks", "--disable-optimized", - "--with-cxx-include-root=" + cxxroot, - "--with-cxx-include-arch=" + cxxarch]), - llvm_gcc=dict(debug=["--prefix=" + self.install_prefix, - "--enable-checking", - "--program-prefix=llvm-", - "--enable-llvm=" + self.build_prefix + "/llvm/" + build_suffix, -# Fortran install seems to be broken. -# "--enable-languages=c,c++,fortran"], - "--enable-languages=c,c++"], - release=["--prefix=" + self.install_prefix, - "--program-prefix=llvm-", - "--enable-llvm=" + self.build_prefix + "/llvm/" + build_suffix, -# Fortran install seems to be broken. -# "--enable-languages=c,c++,fortran"], - "--enable-languages=c,c++"], - paranoid=["--prefix=" + self.install_prefix, - "--enable-checking", - "--program-prefix=llvm-", - "--enable-llvm=" + self.build_prefix + "/llvm/" + build_suffix, -# Fortran install seems to be broken. -# "--enable-languages=c,c++,fortran"]), - "--enable-languages=c,c++"]), - llvm2=dict(debug=["--prefix=" + self.install_prefix, - "--with-extra-options=-Werror", - "--enable-assertions", - "--disable-optimized", - "--with-llvmgccdir=" + self.install_prefix + "/bin", - "--with-cxx-include-root=" + cxxroot, - "--with-cxx-include-arch=" + cxxarch], - release=["--prefix=" + self.install_prefix, - "--with-extra-options=-Werror", - "--enable-optimized", - "--with-llvmgccdir=" + self.install_prefix + "/bin", - "--with-cxx-include-root=" + cxxroot, - "--with-cxx-include-arch=" + cxxarch], - paranoid=["--prefix=" + self.install_prefix, - "--with-extra-options=-Werror", - "--enable-assertions", - "--enable-expensive-checks", - "--disable-optimized", - "--with-llvmgccdir=" + self.install_prefix + "/bin", - "--with-cxx-include-root=" + cxxroot, - "--with-cxx-include-arch=" + cxxarch]), - gcc=dict(debug=["--prefix=" + self.install_prefix, - "--enable-checking"], - release=["--prefix=" + self.install_prefix], - paranoid=["--prefix=" + self.install_prefix, - "--enable-checking"]), + "--with-gcc-toolchain=" + cxxroot]), dragonegg=dict(debug=[], release=[], paranoid=[])) @@ -488,24 +378,6 @@ class Builder(threading.Thread): CXX=self.cxx), paranoid=dict(CC=self.cc, CXX=self.cxx)), - llvm_gcc=dict(debug=dict(CC=self.cc, - CXX=self.cxx), - release=dict(CC=self.cc, - CXX=self.cxx), - paranoid=dict(CC=self.cc, - CXX=self.cxx)), - llvm2=dict(debug=dict(CC=self.cc, - CXX=self.cxx), - release=dict(CC=self.cc, - CXX=self.cxx), - paranoid=dict(CC=self.cc, - CXX=self.cxx)), - gcc=dict(debug=dict(CC=self.cc, - CXX=self.cxx), - release=dict(CC=self.cc, - CXX=self.cxx), - paranoid=dict(CC=self.cc, - CXX=self.cxx)), dragonegg=dict(debug=dict(CC=self.cc, CXX=self.cxx), release=dict(CC=self.cc, @@ -517,21 +389,6 @@ class Builder(threading.Thread): llvm=dict(debug=["-j" + str(self.jobs)], release=["-j" + str(self.jobs)], paranoid=["-j" + str(self.jobs)]), - llvm_gcc=dict(debug=["-j" + str(self.jobs), - "bootstrap"], - release=["-j" + str(self.jobs), - "bootstrap"], - paranoid=["-j" + str(self.jobs), - "bootstrap"]), - llvm2=dict(debug=["-j" + str(self.jobs)], - release=["-j" + str(self.jobs)], - paranoid=["-j" + str(self.jobs)]), - gcc=dict(debug=["-j" + str(self.jobs), - "bootstrap"], - release=["-j" + str(self.jobs), - "bootstrap"], - paranoid=["-j" + str(self.jobs), - "bootstrap"]), dragonegg=dict(debug=["-j" + str(self.jobs)], release=["-j" + str(self.jobs)], paranoid=["-j" + str(self.jobs)])) @@ -540,35 +397,17 @@ class Builder(threading.Thread): llvm=dict(debug=dict(), release=dict(), paranoid=dict()), - llvm_gcc=dict(debug=dict(), - release=dict(), - paranoid=dict()), - llvm2=dict(debug=dict(), - release=dict(), - paranoid=dict()), - gcc=dict(debug=dict(), - release=dict(), - paranoid=dict()), - dragonegg=dict(debug=dict(GCC=self.install_prefix + "/bin/gcc", + dragonegg=dict(debug=dict(GCC=self.cc, LLVM_CONFIG=self.install_prefix + "/bin/llvm-config"), - release=dict(GCC=self.install_prefix + "/bin/gcc", + release=dict(GCC=self.cc, LLVM_CONFIG=self.install_prefix + "/bin/llvm-config"), - paranoid=dict(GCC=self.install_prefix + "/bin/gcc", + paranoid=dict(GCC=self.cc, LLVM_CONFIG=self.install_prefix + "/bin/llvm-config"))) make_install_flags = dict( llvm=dict(debug=["install"], release=["install"], paranoid=["install"]), - llvm_gcc=dict(debug=["install"], - release=["install"], - paranoid=["install"]), - llvm2=dict(debug=["install"], - release=["install"], - paranoid=["install"]), - gcc=dict(debug=["install"], - release=["install"], - paranoid=["install"]), dragonegg=dict(debug=["install"], release=["install"], paranoid=["install"])) @@ -577,15 +416,6 @@ class Builder(threading.Thread): llvm=dict(debug=dict(), release=dict(), paranoid=dict()), - llvm_gcc=dict(debug=dict(), - release=dict(), - paranoid=dict()), - llvm2=dict(debug=dict(), - release=dict(), - paranoid=dict()), - gcc=dict(debug=dict(), - release=dict(), - paranoid=dict()), dragonegg=dict(debug=dict(), release=dict(), paranoid=dict())) @@ -594,15 +424,6 @@ class Builder(threading.Thread): llvm=dict(debug=["check"], release=["check"], paranoid=["check"]), - llvm_gcc=dict(debug=["check"], - release=["check"], - paranoid=["check"]), - llvm2=dict(debug=["check"], - release=["check"], - paranoid=["check"]), - gcc=dict(debug=["check"], - release=["check"], - paranoid=["check"]), dragonegg=dict(debug=["check"], release=["check"], paranoid=["check"])) @@ -611,15 +432,6 @@ class Builder(threading.Thread): llvm=dict(debug=dict(), release=dict(), paranoid=dict()), - llvm_gcc=dict(debug=dict(), - release=dict(), - paranoid=dict()), - llvm2=dict(debug=dict(), - release=dict(), - paranoid=dict()), - gcc=dict(debug=dict(), - release=dict(), - paranoid=dict()), dragonegg=dict(debug=dict(), release=dict(), paranoid=dict())) @@ -627,8 +439,8 @@ class Builder(threading.Thread): for component in components: comp = component[:] - if (self.options.no_gcc): - if (comp == 'gcc' or comp == 'dragonegg' or comp == 'llvm2'): + if (self.options.no_dragonegg): + if (comp == 'dragonegg'): self.logger.info("Skipping " + component + " in " + builddir) continue @@ -637,15 +449,13 @@ class Builder(threading.Thread): builddir = self.build_prefix + "/" + comp + "/" + build_suffix installdir = self.install_prefix - if (branch is not None): - srcdir += "/" + branch - comp_key = comp.replace("-", "_") config_args = configure_flags[comp_key][build][:] config_args.extend(getattr(self.options, "extra_" + comp_key.rstrip("2") - + "_config_flags").split()) + + "_config_flags", + "").split()) self.logger.info("Configuring " + component + " in " + builddir) self.configure(component, srcdir, builddir, @@ -678,12 +488,7 @@ class Builder(threading.Thread): configure_files = dict( llvm=[(srcdir + "/configure", builddir + "/Makefile")], - llvm_gcc=[(srcdir + "/configure", builddir + "/Makefile"), - (srcdir + "/gcc/configure", builddir + "/gcc/Makefile")], - llvm2=[(srcdir + "/configure", builddir + "/Makefile")], - gcc=[(srcdir + "/configure", builddir + "/Makefile"), - (srcdir + "/gcc/configure", builddir + "/gcc/Makefile")], - dragonegg=[()]) + dragonegg=[("","")]) doconfig = False @@ -726,8 +531,7 @@ class Builder(threading.Thread): # Global constants build_abbrev = dict(debug="dbg", release="opt", paranoid="par") -#components = ["llvm", "llvm-gcc", "llvm2", "gcc", "dragonegg"] -components = ["llvm", "llvm2", "gcc", "dragonegg"] +components = ["llvm", "dragonegg"] # Parse options parser = optparse.OptionParser(version="%prog 1.0") @@ -744,10 +548,6 @@ else: source_abbrev = get_path_abbrevs(set(options.src)) -branch_abbrev = None -if options.branch is not None: - branch_abbrev = get_path_abbrevs(set(options.branch)) - work_queue = queue.Queue() jobs = options.jobs // options.threads @@ -760,19 +560,18 @@ logging.getLogger().info("Building with " + str(options.jobs) + " jobs and " + str(numthreads) + " threads using " + str(jobs) + " make jobs") +logging.getLogger().info("CC = " + str(options.cc)) +logging.getLogger().info("CXX = " + str(options.cxx)) + for t in range(numthreads): builder = Builder(work_queue, jobs, - build_abbrev, source_abbrev, branch_abbrev, + build_abbrev, source_abbrev, options) builder.daemon = True builder.start() for build in set(options.build): for source in set(options.src): - if options.branch is not None: - for branch in set(options.branch): - work_queue.put((source, branch, build)) - else: - work_queue.put((source, None, build)) + work_queue.put((source, build)) work_queue.join() diff --git a/utils/llvm.grm b/utils/llvm.grm index fb26dbb..322036b 100644 --- a/utils/llvm.grm +++ b/utils/llvm.grm @@ -174,6 +174,7 @@ FuncAttr ::= noreturn | sspreq | returns_twice | nonlazybind + | address_safety ; OptFuncAttrs ::= + _ | OptFuncAttrs FuncAttr ; diff --git a/utils/llvmgrep b/utils/llvmgrep index 540f059..dc15da4 100755 --- a/utils/llvmgrep +++ b/utils/llvmgrep @@ -29,7 +29,7 @@ if test -d "$TOPDIR" ; then cd $TOPDIR case `uname -s` in SunOS) grep_cmd="ggrep -H -n" ;; - Linux) grep_cmd="egrep -H -n" ;; + Linux|Darwin) grep_cmd="egrep -H -n" ;; *) grep_cmd="egrep -l -n" ;; esac ./utils/llvmdo -topdir "$TOPDIR" \ diff --git a/utils/parseNLT.pl b/utils/parseNLT.pl deleted file mode 100644 index 95afca7..0000000 --- a/utils/parseNLT.pl +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/perl -# a first attempt to parse the nightly tester pages into something -# one can reason about, namely import into a database -# USE: perl parseNLT.pl <2005-03-31.html -# for example - -while(<>) - { - if (/LLVM Test Results for (\w+) (\d+), (\d+)</) - { - $mon = $1; - $day = $2; - $year = $3; - } - if (/<td>([^<]+)<\/td>/) - { - if ($prefix) - { $output .= "$1 "; $count++; } - } - if (/<tr/) - { - if ($output and $count > 3) - { print "\n$day $mon $year $prefix/$output"; } - $output = ""; - $count = 0; - } - if (/<h2>(Programs.+)<\/h2>/) - { - $prefix = $1; - } - } - -if ($output) - { print "\n$day $mon $year $prefix/$output"; $output = ""; } diff --git a/utils/plotNLT.pl b/utils/plotNLT.pl deleted file mode 100644 index 55d503d..0000000 --- a/utils/plotNLT.pl +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/perl -#takes a test and a program from a dp and produces a gnuplot script -#use like perl plotNLT.pl password Programs/MultiSource/Benchmarks/ASCI_Purple/SMG2000/smg2000 llc - -use DBI; - -# database information -$db="llvmalpha"; -$host="localhost"; -$userid="llvmdbuser"; -$passwd=shift @ARGV; -$connectionInfo="dbi:mysql:$db;$host"; - -# make connection to database -$dbh = DBI->connect($connectionInfo,$userid,$passwd) or die DBI->errstr; - - -$count = @ARGV / 2; - -print "set xdata time\n"; -print 'set timefmt "%Y-%m-%d"'; -print "\nplot"; -for ($iter = 0; $iter < $count; $iter++) { - if ($iter) - { print ","; } - print " '-' using 1:2 with lines"; -} - -print "\n"; - -for ($iter = 0; $iter < $count; $iter++) { - - $prog = shift @ARGV; - $test = shift @ARGV; - - $query = "Select RUN, VALUE from Tests where TEST = '$test' AND NAME = '$prog' ORDER BY RUN"; - #print "\n$query\n"; - - my $sth = $dbh->prepare( $query) || die "Can't prepare statement: $DBI::errstr";; - - my $rc = $sth->execute or die DBI->errstr; - - while(($da,$v) = $sth->fetchrow_array) - { - print "$da $v\n"; - } - - print "e\n"; -} - - -# disconnect from database -$dbh->disconnect; diff --git a/utils/release/findRegressions-nightly.py b/utils/release/findRegressions-nightly.py new file mode 100755 index 0000000..e801dab --- /dev/null +++ b/utils/release/findRegressions-nightly.py @@ -0,0 +1,130 @@ +#!/usr/bin/python +import re, string, sys, os, time + +DEBUG = 0 +testDirName = 'llvm-test' +test = ['compile', 'llc', 'jit', 'cbe'] +exectime = ['llc-time', 'jit-time', 'cbe-time',] +comptime = ['llc', 'jit-comptime', 'compile'] + +(tp, exp) = ('compileTime_', 'executeTime_') + +def parse(file): + f=open(file, 'r') + d = f.read() + + #Cleanup weird stuff + d = re.sub(r',\d+:\d','', d) + + r = re.findall(r'TEST-(PASS|FAIL|RESULT.*?):\s+(.*?)\s+(.*?)\r*\n', d) + + test = {} + fname = '' + for t in r: + if DEBUG: + print t + if t[0] == 'PASS' or t[0] == 'FAIL' : + tmp = t[2].split(testDirName) + + if DEBUG: + print tmp + + if len(tmp) == 2: + fname = tmp[1].strip('\r\n') + else: + fname = tmp[0].strip('\r\n') + + if not test.has_key(fname) : + test[fname] = {} + + for k in test: + test[fname][k] = 'NA' + test[fname][t[1]] = t[0] + if DEBUG: + print test[fname][t[1]] + else : + try: + n = t[0].split('RESULT-')[1] + + if DEBUG: + print n; + + if n == 'llc' or n == 'jit-comptime' or n == 'compile': + test[fname][tp + n] = float(t[2].split(' ')[2]) + if DEBUG: + print test[fname][tp + n] + + elif n.endswith('-time') : + test[fname][exp + n] = float(t[2].strip('\r\n')) + if DEBUG: + print test[fname][exp + n] + + else : + print "ERROR!" + sys.exit(1) + + except: + continue + + return test + +# Diff results and look for regressions. +def diffResults(d_old, d_new): + + for t in sorted(d_old.keys()) : + if DEBUG: + print t + + if d_new.has_key(t) : + + # Check if the test passed or failed. + for x in test: + if d_old[t].has_key(x): + if d_new[t].has_key(x): + if d_old[t][x] == 'PASS': + if d_new[t][x] != 'PASS': + print t + " *** REGRESSION (" + x + ")\n" + else: + if d_new[t][x] == 'PASS': + print t + " * NEW PASS (" + x + ")\n" + + else : + print t + "*** REGRESSION (" + x + ")\n" + + # For execution time, if there is no result, its a fail. + for x in exectime: + if d_old[t].has_key(tp + x): + if not d_new[t].has_key(tp + x): + print t + " *** REGRESSION (" + tp + x + ")\n" + + else : + if d_new[t].has_key(tp + x): + print t + " * NEW PASS (" + tp + x + ")\n" + + + for x in comptime: + if d_old[t].has_key(exp + x): + if not d_new[t].has_key(exp + x): + print t + " *** REGRESSION (" + exp + x + ")\n" + + else : + if d_new[t].has_key(exp + x): + print t + " * NEW PASS (" + exp + x + ")\n" + + else : + print t + ": Removed from test-suite.\n" + + +#Main +if len(sys.argv) < 3 : + print 'Usage:', sys.argv[0], \ + '<old log> <new log>' + sys.exit(-1) + +d_old = parse(sys.argv[1]) +d_new = parse(sys.argv[2]) + + +diffResults(d_old, d_new) + + diff --git a/utils/release/findRegressions.py b/utils/release/findRegressions-simple.py index 7629c8b..7586231 100755 --- a/utils/release/findRegressions.py +++ b/utils/release/findRegressions-simple.py @@ -62,6 +62,13 @@ def parse(file): # Diff results and look for regressions. def diffResults(d_old, d_new): + regressions = {} + passes = {} + removed = '' + + for x in ['compile state', 'compile time', 'exec state', 'exec time']: + regressions[x] = '' + passes[x] = '' for t in sorted(d_old.keys()) : if d_new.has_key(t): @@ -77,13 +84,13 @@ def diffResults(d_old, d_new): if d_old[t][x] == 'PASS': if d_new[t][x] != 'PASS': - print t + " *** REGRESSION (" + x + " now fails)" + regressions[x] += t + "\n" else: if d_new[t][x] == 'PASS': - print t + " * NEW PASS (" + x + " now fails)" + passes[x] += t + "\n" else : - print t + "*** REGRESSION (" + x + " now fails)" + regressions[x] += t + "\n" if x == 'compile state' or x == 'exec state': continue @@ -92,25 +99,53 @@ def diffResults(d_old, d_new): if not d_old[t].has_key(x) and not d_new[t].has_key(x): continue elif not d_new[t].has_key(x): - print t + " *** REGRESSION (" + x + ")" + regressions[x] += t + "\n" elif not d_old[t].has_key(x): - print t + " * NEW PASS (" + x + ")" + passes[x] += t + "\n" if math.isnan(d_old[t][x]) and math.isnan(d_new[t][x]): continue elif math.isnan(d_old[t][x]) and not math.isnan(d_new[t][x]): - print t + " * NEW PASS (" + x + ")" + passes[x] += t + "\n" elif not math.isnan(d_old[t][x]) and math.isnan(d_new[t][x]): - print t + " *** REGRESSION (" + x + ")" + regressions[x] += t + ": NaN%\n" - if d_new[t][x] > d_old[t][x] and \ - (d_new[t][x] - d_old[t][x]) / d_new[t][x] > .05: - print t + " *** REGRESSION (" + x + ")" + if d_new[t][x] > d_old[t][x] and d_old[t][x] > 0.0 and \ + (d_new[t][x] - d_old[t][x]) / d_old[t][x] > .05: + regressions[x] += t + ": " + "{0:.1f}".format(100 * (d_new[t][x] - d_old[t][x]) / d_old[t][x]) + "%\n" else : - print t + ": Removed from test-suite." + removed += t + "\n" + + if len(regressions['compile state']) != 0: + print 'REGRESSION: Compilation Failed' + print regressions['compile state'] + + if len(regressions['exec state']) != 0: + print 'REGRESSION: Execution Failed' + print regressions['exec state'] + + if len(regressions['compile time']) != 0: + print 'REGRESSION: Compilation Time' + print regressions['compile time'] + + if len(regressions['exec time']) != 0: + print 'REGRESSION: Execution Time' + print regressions['exec time'] + + if len(passes['compile state']) != 0: + print 'NEW PASSES: Compilation' + print passes['compile state'] + + if len(passes['exec state']) != 0: + print 'NEW PASSES: Execution' + print passes['exec state'] + + if len(removed) != 0: + print 'REMOVED TESTS' + print removed # Main if len(sys.argv) < 3 : diff --git a/utils/release/merge.sh b/utils/release/merge.sh new file mode 100755 index 0000000..2cf39b2 --- /dev/null +++ b/utils/release/merge.sh @@ -0,0 +1,74 @@ +#!/bin/sh +#===-- merge.sh - Test the LLVM release candidates -------------------------===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. +# +#===------------------------------------------------------------------------===# +# +# Merge a revision into a project. +# +#===------------------------------------------------------------------------===# + +set -e + +rev="" +proj="" + +function 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" +} + +while [ $# -gt 0 ]; do + case $1 in + -rev | --rev | -r ) + shift + rev=$1 + ;; + -proj | --proj | -project | --project | -p ) + shift + proj=$1 + ;; + -h | -help | --help ) + usage + ;; + * ) + echo "unknown option: $1" + echo "" + usage + exit 1 + ;; + esac + shift +done + +if [ "x$rev" = "x" -o "x$proj" = "x" ]; then + echo "error: need to specify project and revision" + echo + usage + exit 1 +fi + +if ! svn ls http://llvm.org/svn/llvm-project/$proj/trunk > /dev/null 2>&1 ; then + echo "error: invalid project: $proj" + exit 1 +fi + +tempfile=`mktemp /tmp/merge.XXXXXX` || exit 1 + +echo "Merging r$rev:" > $tempfile +svn log -c $rev http://llvm.org/svn/llvm-project/$proj/trunk >> $tempfile 2>&1 + +cd $proj.src +echo "# Updating tree" +svn up +echo "# Merging r$rev into $proj" +svn merge -c $rev https://llvm.org/svn/llvm-project/$proj/trunk . || exit 1 +echo "# Committing changes" +svn commit -F $tempfile || exit 1 +rm -f $tempfile +exit 0 diff --git a/utils/release/tag.sh b/utils/release/tag.sh new file mode 100755 index 0000000..80da47a --- /dev/null +++ b/utils/release/tag.sh @@ -0,0 +1,99 @@ +#!/bin/sh +#===-- tag.sh - Tag the LLVM release candidates ----------------------------===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. +# +#===------------------------------------------------------------------------===# +# +# Create branches and release candidates for the LLVM release. +# +#===------------------------------------------------------------------------===# + +set -e + +release="" +rc="" + +base_url="https://llvm.org/svn/llvm-project" + +function usage() { + echo "usage: `basename $0` -release <num>" + echo "usage: `basename $0` -release <num> -rc <num>" + echo " " + echo " -release <num> The version number of the release" + echo " -rc <num> The release candidate number" + echo " -final Tag final release candidate" +} + +function tag_version() { + set -x + for proj in llvm cfe dragonegg test-suite compiler-rt libcxx libcxxabi ; do + if ! svn ls $base_url/$proj/branches/release_$release > /dev/null 2>&1 ; then + svn copy -m "Creating release_$release branch" \ + $base_url/$proj/trunk \ + $base_url/$proj/branches/release_$release + fi + done + set +x +} + +function tag_release_candidate() { + set -x + for proj in llvm cfe dragonegg test-suite compiler-rt libcxx libcxxabi ; do + if ! svn ls $base_url/$proj/tags/RELEASE_$release > /dev/null 2>&1 ; then + svn mkdir -m "Creating release directory for release_$release." $base_url/$proj/tags/RELEASE_$release + fi + if ! svn ls $base_url/$proj/tags/RELEASE_$release/$rc > /dev/null 2>&1 ; then + svn copy -m "Creating release candidate $rc from release_$release branch" \ + $base_url/$proj/branches/release_$release \ + $base_url/$proj/tags/RELEASE_$release/$rc + fi + done + set +x +} + +while [ $# -gt 0 ]; do + case $1 in + -release | --release ) + shift + release=$1 + ;; + -rc | --rc ) + shift + rc="rc$1" + ;; + -final | --final ) + rc="final" + ;; + -h | --help | -help ) + usage + exit 0 + ;; + * ) + echo "unknown option: $1" + usage + exit 1 + ;; + esac + shift +done + +if [ "x$release" = "x" ]; then + echo "error: need to specify a release version" + echo + usage + exit 1 +fi + +release=`echo $release | sed -e 's,\.,,g'` + +if [ "x$rc" = "x" ]; then + tag_version +else + tag_release_candidate +fi + +exit 1 diff --git a/utils/release/test-release.sh b/utils/release/test-release.sh index 94217e5..ad1af5f 100755 --- a/utils/release/test-release.sh +++ b/utils/release/test-release.sh @@ -28,11 +28,14 @@ Release_no_dot="" RC="" do_checkout="yes" do_ada="no" -do_objc="yes" +do_clang="yes" +do_dragonegg="no" do_fortran="no" +do_objc="yes" do_64bit="yes" do_debug="no" do_asserts="no" +do_compare="yes" BuildDir="`pwd`" function usage() { @@ -40,15 +43,19 @@ function usage() { echo "" echo " -release X.Y The release number to test." echo " -rc NUM The pre-release candidate number." + echo " -final The final release candidate." echo " -j NUM Number of compile jobs to run. [default: 3]" echo " -build-dir DIR Directory to perform testing in. [default: pwd]" echo " -no-checkout Don't checkout the sources from SVN." echo " -no-64bit Don't test the 64-bit version. [default: yes]" echo " -enable-ada Build Ada. [default: disable]" + echo " -disable-clang Do not test clang. [default: enable]" + echo " -enable-dragonegg Test dragonegg. [default: disable]" echo " -enable-fortran Enable Fortran build. [default: disable]" echo " -disable-objc Disable ObjC build. [default: enable]" echo " -test-debug Test the debug build. [default: no]" echo " -test-asserts Test with asserts on. [default: no]" + echo " -no-compare-files Don't test that phase 2 and 3 files are identical." } while [ $# -gt 0 ]; do @@ -60,7 +67,10 @@ while [ $# -gt 0 ]; do ;; -rc | --rc | -RC | --RC ) shift - RC=$1 + RC="rc$1" + ;; + -final | --final ) + RC=final ;; -j* ) NumJobs="`echo $1 | sed -e 's,-j\([0-9]*\),\1,g'`" @@ -82,6 +92,12 @@ while [ $# -gt 0 ]; do -enable-ada | --enable-ada ) do_ada="yes" ;; + -disable-clang | --disable-clang ) + do_clang="no" + ;; + -enable-dragonegg | --enable-dragonegg ) + do_dragonegg="yes" + ;; -enable-fortran | --enable-fortran ) do_fortran="yes" ;; @@ -94,6 +110,9 @@ while [ $# -gt 0 ]; do -test-asserts | --test-asserts ) do_asserts="yes" ;; + -no-compare-files | --no-compare-files ) + do_compare="no" + ;; -help | --help | -h | --h | -\? ) usage exit 0 @@ -132,7 +151,7 @@ if [ -z "$NumJobs" ]; then fi # Go to the build directory (may be different from CWD) -BuildDir=$BuildDir/rc$RC +BuildDir=$BuildDir/$RC mkdir -p $BuildDir cd $BuildDir @@ -140,16 +159,34 @@ cd $BuildDir LogDir=$BuildDir/logs mkdir -p $LogDir -# Find a compilers. -c_compiler="$CC" -cxx_compiler="$CXX" +# Find compilers. +if [ "$do_dragonegg" = "yes" ]; then + gcc_compiler="$GCC" + if [ -z "$gcc_compiler" ]; then + gcc_compiler="`which gcc`" + if [ -z "$gcc_compiler" ]; then + echo "error: cannot find gcc to use with dragonegg" + exit 1 + fi + fi + + gxx_compiler="$GXX" + if [ -z "$gxx_compiler" ]; then + gxx_compiler="`which g++`" + if [ -z "$gxx_compiler" ]; then + echo "error: cannot find g++ to use with dragonegg" + exit 1 + fi + fi +fi + # Make sure that the URLs are valid. function check_valid_urls() { for proj in $projects ; do echo "# Validating $proj SVN URL" - if ! svn ls $Base_url/$proj/tags/RELEASE_$Release_no_dot/rc$RC > /dev/null 2>&1 ; then + if ! svn ls $Base_url/$proj/tags/RELEASE_$Release_no_dot/$RC > /dev/null 2>&1 ; then echo "llvm $Release release candidate $RC doesn't exist!" exit 1 fi @@ -162,7 +199,7 @@ function export_sources() { for proj in $projects ; do echo "# Exporting $proj $Release-RC$RC sources" - if ! svn export -q $Base_url/$proj/tags/RELEASE_$Release_no_dot/rc$RC $proj.src ; then + if ! svn export -q $Base_url/$proj/tags/RELEASE_$Release_no_dot/$RC $proj.src ; then echo "error: failed to export $proj project" exit 1 fi @@ -210,14 +247,15 @@ function configure_llvmCore() { echo "# Using C++ compiler: $cxx_compiler" cd $ObjDir - echo "# Configuring llvm $Release-rc$RC $Flavor" + echo "# Configuring llvm $Release-$RC $Flavor" echo "# $BuildDir/llvm.src/configure --prefix=$InstallDir \ --enable-optimized=$Optimized \ --enable-assertions=$Assertions" - env CC=$c_compiler CXX=$cxx_compiler \ + env CC="$c_compiler" CXX="$cxx_compiler" \ $BuildDir/llvm.src/configure --prefix=$InstallDir \ --enable-optimized=$Optimized \ --enable-assertions=$Assertions \ + --disable-timestamps \ 2>&1 | tee $LogDir/llvm.configure-Phase$Phase-$Flavor.log cd $BuildDir } @@ -233,18 +271,40 @@ function build_llvmCore() { fi cd $ObjDir - echo "# Compiling llvm $Release-rc$RC $Flavor" + echo "# Compiling llvm $Release-$RC $Flavor" echo "# ${MAKE} -j $NumJobs VERBOSE=1 $ExtraOpts" ${MAKE} -j $NumJobs VERBOSE=1 $ExtraOpts \ 2>&1 | tee $LogDir/llvm.make-Phase$Phase-$Flavor.log - echo "# Installing llvm $Release-rc$RC $Flavor" + echo "# Installing llvm $Release-$RC $Flavor" echo "# ${MAKE} install" ${MAKE} install \ 2>&1 | tee $LogDir/llvm.install-Phase$Phase-$Flavor.log cd $BuildDir } +function build_dragonegg() { + Phase="$1" + Flavor="$2" + LLVMInstallDir="$3" + DragonEggObjDir="$4" + LLVM_CONFIG=$LLVMInstallDir/bin/llvm-config + TOP_DIR=$BuildDir/dragonegg.src + + echo "# Targeted compiler: $gcc_compiler" + + cd $DragonEggObjDir + echo "# Compiling phase $Phase dragonegg $Release-$RC $Flavor" + echo -n "# CXX=$cxx_compiler TOP_DIR=$TOP_DIR GCC=$gcc_compiler " + echo -n "LLVM_CONFIG=$LLVM_CONFIG ${MAKE} -f $TOP_DIR/Makefile " + echo "-j $NumJobs VERBOSE=1" + CXX="$cxx_compiler" TOP_DIR="$TOP_DIR" GCC="$gcc_compiler" \ + LLVM_CONFIG="$LLVM_CONFIG" ${MAKE} -f $TOP_DIR/Makefile \ + -j $NumJobs VERBOSE=1 \ + 2>&1 | tee $LogDir/dragonegg-Phase$Phase-$Flavor.log + cd $BuildDir +} + function test_llvmCore() { Phase="$1" Flavor="$2" @@ -280,81 +340,173 @@ for Flavor in $Flavors ; do echo "" echo "" echo "********************************************************************************" - echo " Release: $Release-rc$RC" + echo " Release: $Release-$RC" echo " Build: $Flavor" echo " System Info: " echo " `uname -a`" echo "********************************************************************************" echo "" - llvmCore_phase1_objdir=$BuildDir/Phase1/$Flavor/llvmCore-$Release-rc$RC.obj - llvmCore_phase1_installdir=$BuildDir/Phase1/$Flavor/llvmCore-$Release-rc$RC.install + c_compiler="$CC" + cxx_compiler="$CXX" + + llvmCore_phase1_objdir=$BuildDir/Phase1/$Flavor/llvmCore-$Release-$RC.obj + llvmCore_phase1_installdir=$BuildDir/Phase1/$Flavor/llvmCore-$Release-$RC.install + dragonegg_phase1_objdir=$BuildDir/Phase1/$Flavor/DragonEgg-$Release-$RC.obj - llvmCore_phase2_objdir=$BuildDir/Phase2/$Flavor/llvmCore-$Release-rc$RC.obj - llvmCore_phase2_installdir=$BuildDir/Phase2/$Flavor/llvmCore-$Release-rc$RC.install + llvmCore_phase2_objdir=$BuildDir/Phase2/$Flavor/llvmCore-$Release-$RC.obj + llvmCore_phase2_installdir=$BuildDir/Phase2/$Flavor/llvmCore-$Release-$RC.install + llvmCore_de_phase2_objdir=$BuildDir/Phase2/$Flavor/llvmCore-DragonEgg-$Release-$RC.obj + llvmCore_de_phase2_installdir=$BuildDir/Phase2/$Flavor/llvmCore-DragonEgg-$Release-$RC.install + dragonegg_phase2_objdir=$BuildDir/Phase2/$Flavor/DragonEgg-$Release-$RC.obj - llvmCore_phase3_objdir=$BuildDir/Phase3/$Flavor/llvmCore-$Release-rc$RC.obj - llvmCore_phase3_installdir=$BuildDir/Phase3/$Flavor/llvmCore-$Release-rc$RC.install + llvmCore_phase3_objdir=$BuildDir/Phase3/$Flavor/llvmCore-$Release-$RC.obj + llvmCore_phase3_installdir=$BuildDir/Phase3/$Flavor/llvmCore-$Release-$RC.install + llvmCore_de_phase3_objdir=$BuildDir/Phase3/$Flavor/llvmCore-DragonEgg-$Release-$RC.obj + llvmCore_de_phase3_installdir=$BuildDir/Phase3/$Flavor/llvmCore-DragonEgg-$Release-$RC.install + dragonegg_phase3_objdir=$BuildDir/Phase3/$Flavor/DragonEgg-$Release-$RC.obj rm -rf $llvmCore_phase1_objdir rm -rf $llvmCore_phase1_installdir + rm -rf $dragonegg_phase1_objdir + rm -rf $llvmCore_phase2_objdir rm -rf $llvmCore_phase2_installdir + rm -rf $llvmCore_de_phase2_objdir + rm -rf $llvmCore_de_phase2_installdir + rm -rf $dragonegg_phase2_objdir + rm -rf $llvmCore_phase3_objdir rm -rf $llvmCore_phase3_installdir + rm -rf $llvmCore_de_phase3_objdir + rm -rf $llvmCore_de_phase3_installdir + rm -rf $dragonegg_phase3_objdir mkdir -p $llvmCore_phase1_objdir mkdir -p $llvmCore_phase1_installdir + mkdir -p $dragonegg_phase1_objdir + mkdir -p $llvmCore_phase2_objdir mkdir -p $llvmCore_phase2_installdir + mkdir -p $llvmCore_de_phase2_objdir + mkdir -p $llvmCore_de_phase2_installdir + mkdir -p $dragonegg_phase2_objdir + mkdir -p $llvmCore_phase3_objdir mkdir -p $llvmCore_phase3_installdir + mkdir -p $llvmCore_de_phase3_objdir + mkdir -p $llvmCore_de_phase3_installdir + mkdir -p $dragonegg_phase3_objdir ############################################################################ - # Phase 1: Build llvmCore and llvmgcc42 + # Phase 1: Build llvmCore and clang echo "# Phase 1: Building llvmCore" configure_llvmCore 1 $Flavor \ $llvmCore_phase1_objdir $llvmCore_phase1_installdir build_llvmCore 1 $Flavor \ $llvmCore_phase1_objdir - ############################################################################ - # Phase 2: Build llvmCore with newly built clang from phase 1. - c_compiler=$llvmCore_phase1_installdir/bin/clang - cxx_compiler=$llvmCore_phase1_installdir/bin/clang++ - echo "# Phase 2: Building llvmCore" - configure_llvmCore 2 $Flavor \ - $llvmCore_phase2_objdir $llvmCore_phase2_installdir - build_llvmCore 2 $Flavor \ - $llvmCore_phase2_objdir - - ############################################################################ - # Phase 3: Build llvmCore with newly built clang from phase 2. - c_compiler=$llvmCore_phase2_installdir/bin/clang - cxx_compiler=$llvmCore_phase2_installdir/bin/clang++ - echo "# Phase 3: Building llvmCore" - configure_llvmCore 3 $Flavor \ - $llvmCore_phase3_objdir $llvmCore_phase3_installdir - build_llvmCore 3 $Flavor \ - $llvmCore_phase3_objdir + # Test clang + if [ "$do_clang" = "yes" ]; then + ######################################################################## + # Phase 2: Build llvmCore with newly built clang from phase 1. + c_compiler=$llvmCore_phase1_installdir/bin/clang + cxx_compiler=$llvmCore_phase1_installdir/bin/clang++ + echo "# Phase 2: Building llvmCore" + configure_llvmCore 2 $Flavor \ + $llvmCore_phase2_objdir $llvmCore_phase2_installdir + build_llvmCore 2 $Flavor \ + $llvmCore_phase2_objdir + + ######################################################################## + # Phase 3: Build llvmCore with newly built clang from phase 2. + c_compiler=$llvmCore_phase2_installdir/bin/clang + cxx_compiler=$llvmCore_phase2_installdir/bin/clang++ + echo "# Phase 3: Building llvmCore" + configure_llvmCore 3 $Flavor \ + $llvmCore_phase3_objdir $llvmCore_phase3_installdir + build_llvmCore 3 $Flavor \ + $llvmCore_phase3_objdir + + ######################################################################## + # Testing: Test phase 3 + echo "# Testing - built with clang" + test_llvmCore 3 $Flavor $llvmCore_phase3_objdir + + ######################################################################## + # Compare .o files between Phase2 and Phase3 and report which ones + # differ. + if [ "$do_compare" = "yes" ]; then + echo + echo "# Comparing Phase 2 and Phase 3 files" + for o in `find $llvmCore_phase2_objdir -name '*.o'` ; do + p3=`echo $o | sed -e 's,Phase2,Phase3,'` + if ! cmp --ignore-initial=16 $o $p3 > /dev/null 2>&1 ; then + echo "file `basename $o` differs between phase 2 and phase 3" + fi + done + fi + fi - ############################################################################ - # Testing: Test phase 3 - echo "# Testing - built with clang" - test_llvmCore 3 $Flavor $llvmCore_phase3_objdir + # Test dragonegg + if [ "$do_dragonegg" = "yes" ]; then + # Build dragonegg using the targeted gcc. This isn't necessary, but + # helps avoid using broken versions of gcc (which are legion), tests + # that the targeted gcc is basically sane and is consistent with the + # later phases in which the targeted gcc + dragonegg are used. + c_compiler="$gcc_compiler" + cxx_compiler="$gxx_compiler" + build_dragonegg 1 $Flavor $llvmCore_phase1_installdir $dragonegg_phase1_objdir + + ######################################################################## + # Phase 2: Build llvmCore with newly built dragonegg from phase 1. + c_compiler="$gcc_compiler -fplugin=$dragonegg_phase1_objdir/dragonegg.so" + cxx_compiler="$gxx_compiler -fplugin=$dragonegg_phase1_objdir/dragonegg.so" + echo "# Phase 2: Building llvmCore with dragonegg" + configure_llvmCore 2 $Flavor \ + $llvmCore_de_phase2_objdir $llvmCore_de_phase2_installdir + build_llvmCore 2 $Flavor \ + $llvmCore_de_phase2_objdir + build_dragonegg 2 $Flavor $llvmCore_de_phase2_installdir $dragonegg_phase2_objdir + + ######################################################################## + # Phase 3: Build llvmCore with newly built clang from phase 2. + c_compiler="$gcc_compiler -fplugin=$dragonegg_phase2_objdir/dragonegg.so" + cxx_compiler="$gxx_compiler -fplugin=$dragonegg_phase2_objdir/dragonegg.so" + echo "# Phase 3: Building llvmCore with dragonegg" + configure_llvmCore 3 $Flavor \ + $llvmCore_de_phase3_objdir $llvmCore_de_phase3_installdir + build_llvmCore 3 $Flavor \ + $llvmCore_de_phase3_objdir + build_dragonegg 3 $Flavor $llvmCore_de_phase3_installdir $dragonegg_phase3_objdir + + ######################################################################## + # Testing: Test phase 3 + c_compiler="$gcc_compiler -fplugin=$dragonegg_phase3_objdir/dragonegg.so" + cxx_compiler="$gxx_compiler -fplugin=$dragonegg_phase3_objdir/dragonegg.so" + echo "# Testing - built with dragonegg" + test_llvmCore 3 $Flavor $llvmCore_de_phase3_objdir + + ######################################################################## + # Compare .o files between Phase2 and Phase3 and report which ones differ. + echo + echo "# Comparing Phase 2 and Phase 3 files" + for o in `find $llvmCore_de_phase2_objdir -name '*.o'` \ + `find $dragonegg_phase2_objdir -name '*.o'` ; do + p3=`echo $o | sed -e 's,Phase2,Phase3,'` + if ! cmp --ignore-initial=16 $o $p3 > /dev/null 2>&1 ; then + echo "file `basename $o` differs between dragonegg phase 2 and phase 3" + fi + done + fi - ############################################################################ - # Compare .o files between Phase2 and Phase3 and report which ones differ. - echo - echo "# Comparing Phase 2 and Phase 3 files" - for o in `find $llvmCore_phase2_objdir -name '*.o'` ; do - p3=`echo $o | sed -e 's,Phase2,Phase3,'` - if ! cmp --ignore-initial=16 $o $p3 > /dev/null 2>&1 ; then - echo "file `basename $o` differs between phase 2 and phase 3" - fi - done + # Otherwise just test the core. + if [ "$do_clang" != "yes" -a "$do_dragonegg" != "yes" ]; then + echo "# Testing - built with system compiler" + test_llvmCore 1 $Flavor $llvmCore_phase1_objdir + fi done -) 2>&1 | tee $LogDir/testing.$Release-rc$RC.log +) 2>&1 | tee $LogDir/testing.$Release-$RC.log set +e diff --git a/utils/show-diagnostics b/utils/show-diagnostics deleted file mode 100755 index 3a69793..0000000 --- a/utils/show-diagnostics +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python - -import plistlib - -def main(): - from optparse import OptionParser, OptionGroup - parser = OptionParser("""\ -usage: %prog [options] <path> - -Utility for dumping Clang-style logged diagnostics.\ -""") - (opts, args) = parser.parse_args() - - if len(args) != 1: - parser.error("invalid number of arguments") - - path, = args - - # Read the diagnostics log. - f = open(path) - try: - data = f.read() - finally: - f.close() - - # Complete the plist (the log itself is just the chunks). - data = """\ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" \ - "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> -<plist version="1.0"> -<array> -%s -</array> -</plist>""" % data - - # Load the diagnostics. - diags = plistlib.readPlistFromString(data) - - # Print out the diagnostics. - print - print "**** BUILD DIAGNOSTICS ****" - for i, file_diags in enumerate(diags): - file = file_diags.get('main-file') - print "*** %s ***" % file - for d in file_diags.get('diagnostics', ()): - print "%s:%s:%s: %s: %s" % ( - d.get('filename'), d.get('line'), d.get('column'), - d.get('level'), d.get('message')) - -if __name__ == "__main__": - main() diff --git a/utils/unittest/LLVMBuild.txt b/utils/unittest/LLVMBuild.txt new file mode 100644 index 0000000..2810567 --- /dev/null +++ b/utils/unittest/LLVMBuild.txt @@ -0,0 +1,28 @@ +;===- ./utils/unittest/LLVMBuild.txt ---------------------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Library +name = gtest +parent = Libraries +required_libraries = Support + +[component_1] +type = Library +name = gtest_main +parent = Libraries +required_libraries = gtest diff --git a/utils/unittest/UnitTestMain/Makefile b/utils/unittest/UnitTestMain/Makefile index 3082779..7bcb724 100644 --- a/utils/unittest/UnitTestMain/Makefile +++ b/utils/unittest/UnitTestMain/Makefile @@ -11,7 +11,7 @@ LEVEL = ../../.. include $(LEVEL)/Makefile.config -LIBRARYNAME = UnitTestMain +LIBRARYNAME = gtest_main BUILD_ARCHIVE = 1 REQUIRES_RTTI = 1 diff --git a/utils/unittest/googletest/Makefile b/utils/unittest/googletest/Makefile index 21b29ff..22c8f36 100644 --- a/utils/unittest/googletest/Makefile +++ b/utils/unittest/googletest/Makefile @@ -11,7 +11,7 @@ LEVEL := ../../.. include $(LEVEL)/Makefile.config -LIBRARYNAME = GoogleTest +LIBRARYNAME = gtest BUILD_ARCHIVE = 1 REQUIRES_RTTI = 1 diff --git a/utils/unittest/googletest/gtest-death-test.cc b/utils/unittest/googletest/gtest-death-test.cc index 6589385..bf7e32c 100644 --- a/utils/unittest/googletest/gtest-death-test.cc +++ b/utils/unittest/googletest/gtest-death-test.cc @@ -527,7 +527,6 @@ bool DeathTestImpl::Passed(bool status_ok) { } break; case IN_PROGRESS: - default: GTEST_LOG_(FATAL) << "DeathTest::Passed somehow called before conclusion of test"; } diff --git a/utils/unittest/googletest/gtest.cc b/utils/unittest/googletest/gtest.cc index 7624497..3fdff0a 100644 --- a/utils/unittest/googletest/gtest.cc +++ b/utils/unittest/googletest/gtest.cc @@ -2480,9 +2480,10 @@ static const char * TestPartResultTypeToString(TestPartResult::Type type) { #else return "Failure\n"; #endif - default: - return "Unknown result type"; } + + // All cases return, so this is unreachable but GCC doesn't know it + abort(); } // Prints a TestPartResult to a String. diff --git a/utils/unittest/googletest/include/gtest/internal/gtest-death-test-internal.h b/utils/unittest/googletest/include/gtest/internal/gtest-death-test-internal.h index 1d9f83b..7bac2bd 100644 --- a/utils/unittest/googletest/include/gtest/internal/gtest-death-test-internal.h +++ b/utils/unittest/googletest/include/gtest/internal/gtest-death-test-internal.h @@ -207,8 +207,6 @@ GTEST_API_ bool ExitedUnsuccessfully(int exit_status); gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE); \ break; \ } \ - default: \ - break; \ } \ } \ } else \ diff --git a/utils/vim/tablegen.vim b/utils/vim/tablegen.vim index fed619a..40d8d78 100644 --- a/utils/vim/tablegen.vim +++ b/utils/vim/tablegen.vim @@ -1,7 +1,7 @@ " Vim syntax file " Language: TableGen " Maintainer: The LLVM team, http://llvm.org/ -" Version: $Revision: 141378 $ +" Version: $Revision: 151164 $ if version < 600 syntax clear @@ -14,7 +14,7 @@ syntax sync minlines=100 syn case match -syn keyword tgKeyword def let in code dag field include defm +syn keyword tgKeyword def let in code dag field include defm foreach syn keyword tgType class int string list bit bits multiclass syn match tgNumber /\<\d\+\>/ diff --git a/utils/webNLT.pl b/utils/webNLT.pl deleted file mode 100755 index fb29fd2..0000000 --- a/utils/webNLT.pl +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/perl - -use DBI; -use CGI; - -$q = new CGI; -print $q->header(); -print $q->start_html(-title=>"Nightly Tester DB"); - -unless($q->param('pwd')) - { - print $q->startform(); - print $q->password_field(-name=>"pwd", -size=>20, -maxlength=>20); - print $q->submit(); - print $q->endform(); - } -else - { - # database information - $db="llvmalpha"; - $host="localhost"; - $userid="llvmdbuser"; - $passwd=$q->param('pwd'); - $connectionInfo="dbi:mysql:$db;$host"; - - # make connection to database - $dbh = DBI->connect($connectionInfo,$userid,$passwd) or die DBI->errstr; - $query = "Select DISTINCT(NAME) from Tests"; - my $sth = $dbh->prepare($query) || die "Can't prepare statement: $DBI::errstr"; - my $rc = $sth->execute or die DBI->errstr; - while (($n) = $sth->fetchrow_array) - { - push @names, ($n); -# print "$n<P>"; - } - $query = "Select DISTINCT(TEST) from Tests"; - my $sth = $dbh->prepare($query) || die "Can't prepare statement: $DBI::errstr"; - my $rc = $sth->execute or die DBI->errstr; - while (($n) = $sth->fetchrow_array) - { - push @tests, ($n); -# print "$n\n"; - } - -# print join "<BR>", @names; - - print $q->startform(); - print $q->scrolling_list(-name=>"test", -values=>\@tests, -multiple=>'true'); - print "<P>"; - print $q->scrolling_list(-name=>"name", -values=>\@names, -multiple=>'true'); - print "<P>"; - print $q->submit(); - print $q->hidden("pwd", $q->param('pwd')); - print $q->endform(); - - # disconnect from database - $dbh->disconnect; - - #now generate the urls to the chart - if ($q->param('test') && $q->param('name')) - { - my @names = $q->param('name'); - my @tests = $q->param('test'); - print "<P>"; - print join "<BR>", @names; - print "<P>"; - print join "<BR>", @tests; - print "<P>"; - $str = "pwd=" . $q->param('pwd'); - $count = 0; - foreach $n (@names) - { - foreach $t (@tests) - { - $str = "$str&t$count=$t&n$count=$n"; - $count++; - } - } - print "<img src=\"cgiplotNLT.pl?$str\">"; - } - } - -print $q->end_html(); diff --git a/utils/yaml-bench/CMakeLists.txt b/utils/yaml-bench/CMakeLists.txt new file mode 100644 index 0000000..403182c --- /dev/null +++ b/utils/yaml-bench/CMakeLists.txt @@ -0,0 +1,5 @@ +add_llvm_utility(yaml-bench + YAMLBench.cpp + ) + +target_link_libraries(yaml-bench LLVMSupport) diff --git a/utils/yaml-bench/Makefile b/utils/yaml-bench/Makefile new file mode 100644 index 0000000..07e9122 --- /dev/null +++ b/utils/yaml-bench/Makefile @@ -0,0 +1,20 @@ +##===- 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 diff --git a/utils/yaml-bench/YAMLBench.cpp b/utils/yaml-bench/YAMLBench.cpp new file mode 100644 index 0000000..e5ee52a --- /dev/null +++ b/utils/yaml-bench/YAMLBench.cpp @@ -0,0 +1,203 @@ +//===- YAMLBench - Benchmark the YAMLParser implementation ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This program executes the YAMLParser on differntly sized YAML texts and +// outputs the run time. +// +//===----------------------------------------------------------------------===// + + +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/system_error.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/YAMLParser.h" + +using namespace llvm; + +static cl::opt<bool> + DumpTokens( "tokens" + , cl::desc("Print the tokenization of the file.") + , cl::init(false) + ); + +static cl::opt<bool> + DumpCanonical( "canonical" + , cl::desc("Print the canonical YAML for this file.") + , cl::init(false) + ); + +static cl::opt<std::string> + Input(cl::Positional, cl::desc("<input>")); + +static cl::opt<bool> + Verify( "verify" + , cl::desc( + "Run a quick verification useful for regression testing") + , cl::init(false) + ); + +static cl::opt<unsigned> + MemoryLimitMB("memory-limit", cl::desc( + "Do not use more megabytes of memory"), + cl::init(1000)); + +struct indent { + unsigned distance; + indent(unsigned d) : distance(d) {} +}; + +static raw_ostream &operator <<(raw_ostream &os, const indent &in) { + for (unsigned i = 0; i < in.distance; ++i) + os << " "; + return os; +} + +static void dumpNode( yaml::Node *n + , unsigned Indent = 0 + , bool SuppressFirstIndent = false) { + if (!n) + return; + if (!SuppressFirstIndent) + outs() << indent(Indent); + StringRef Anchor = n->getAnchor(); + if (!Anchor.empty()) + outs() << "&" << Anchor << " "; + if (yaml::ScalarNode *sn = dyn_cast<yaml::ScalarNode>(n)) { + SmallString<32> Storage; + StringRef Val = sn->getValue(Storage); + outs() << "!!str \"" << yaml::escape(Val) << "\""; + } else if (yaml::SequenceNode *sn = dyn_cast<yaml::SequenceNode>(n)) { + outs() << "!!seq [\n"; + ++Indent; + for (yaml::SequenceNode::iterator i = sn->begin(), e = sn->end(); + i != e; ++i) { + dumpNode(i, Indent); + outs() << ",\n"; + } + --Indent; + outs() << indent(Indent) << "]"; + } else if (yaml::MappingNode *mn = dyn_cast<yaml::MappingNode>(n)) { + outs() << "!!map {\n"; + ++Indent; + for (yaml::MappingNode::iterator i = mn->begin(), e = mn->end(); + i != e; ++i) { + outs() << indent(Indent) << "? "; + dumpNode(i->getKey(), Indent, true); + outs() << "\n"; + outs() << indent(Indent) << ": "; + dumpNode(i->getValue(), Indent, true); + outs() << ",\n"; + } + --Indent; + outs() << indent(Indent) << "}"; + } else if (yaml::AliasNode *an = dyn_cast<yaml::AliasNode>(n)){ + outs() << "*" << an->getName(); + } else if (dyn_cast<yaml::NullNode>(n)) { + outs() << "!!null null"; + } +} + +static void dumpStream(yaml::Stream &stream) { + for (yaml::document_iterator di = stream.begin(), de = stream.end(); di != de; + ++di) { + outs() << "%YAML 1.2\n" + << "---\n"; + yaml::Node *n = di->getRoot(); + if (n) + dumpNode(n); + else + break; + outs() << "\n...\n"; + } +} + +static void benchmark( llvm::TimerGroup &Group + , llvm::StringRef Name + , llvm::StringRef JSONText) { + llvm::Timer BaseLine((Name + ": Loop").str(), Group); + BaseLine.startTimer(); + char C = 0; + for (llvm::StringRef::iterator I = JSONText.begin(), + E = JSONText.end(); + I != E; ++I) { C += *I; } + BaseLine.stopTimer(); + volatile char DontOptimizeOut = C; (void)DontOptimizeOut; + + llvm::Timer Tokenizing((Name + ": Tokenizing").str(), Group); + Tokenizing.startTimer(); + { + yaml::scanTokens(JSONText); + } + Tokenizing.stopTimer(); + + llvm::Timer Parsing((Name + ": Parsing").str(), Group); + Parsing.startTimer(); + { + llvm::SourceMgr SM; + llvm::yaml::Stream stream(JSONText, SM); + stream.skip(); + } + Parsing.stopTimer(); +} + +static std::string createJSONText(size_t MemoryMB, unsigned ValueSize) { + std::string JSONText; + llvm::raw_string_ostream Stream(JSONText); + Stream << "[\n"; + size_t MemoryBytes = MemoryMB * 1024 * 1024; + while (JSONText.size() < MemoryBytes) { + Stream << " {\n" + << " \"key1\": \"" << std::string(ValueSize, '*') << "\",\n" + << " \"key2\": \"" << std::string(ValueSize, '*') << "\",\n" + << " \"key3\": \"" << std::string(ValueSize, '*') << "\"\n" + << " }"; + Stream.flush(); + if (JSONText.size() < MemoryBytes) Stream << ","; + Stream << "\n"; + } + Stream << "]\n"; + Stream.flush(); + return JSONText; +} + +int main(int argc, char **argv) { + llvm::cl::ParseCommandLineOptions(argc, argv); + if (Input.getNumOccurrences()) { + OwningPtr<MemoryBuffer> Buf; + if (MemoryBuffer::getFileOrSTDIN(Input, Buf)) + return 1; + + llvm::SourceMgr sm; + if (DumpTokens) { + yaml::dumpTokens(Buf->getBuffer(), outs()); + } + + if (DumpCanonical) { + yaml::Stream stream(Buf->getBuffer(), sm); + dumpStream(stream); + } + } + + if (Verify) { + llvm::TimerGroup Group("YAML parser benchmark"); + benchmark(Group, "Fast", createJSONText(10, 500)); + } else if (!DumpCanonical && !DumpTokens) { + llvm::TimerGroup Group("YAML parser benchmark"); + benchmark(Group, "Small Values", createJSONText(MemoryLimitMB, 5)); + benchmark(Group, "Medium Values", createJSONText(MemoryLimitMB, 500)); + benchmark(Group, "Large Values", createJSONText(MemoryLimitMB, 50000)); + } + + return 0; +} |