summaryrefslogtreecommitdiffstats
path: root/utils
diff options
context:
space:
mode:
Diffstat (limited to 'utils')
-rw-r--r--utils/TableGen/CMakeLists.txt2
-rw-r--r--utils/TableGen/CodeEmitterGen.cpp15
-rw-r--r--utils/TableGen/CodeEmitterGen.h3
-rw-r--r--utils/TableGen/CodeGenDAGPatterns.cpp5
-rw-r--r--utils/TableGen/CodeGenInstruction.cpp78
-rw-r--r--utils/TableGen/DAGISelEmitter.cpp4
-rw-r--r--utils/TableGen/DisassemblerEmitter.cpp99
-rw-r--r--utils/TableGen/LLVMCConfigurationEmitter.cpp469
-rw-r--r--utils/TableGen/Record.cpp4
-rw-r--r--utils/TableGen/X86DisassemblerShared.h38
-rw-r--r--utils/TableGen/X86DisassemblerTables.cpp603
-rw-r--r--utils/TableGen/X86DisassemblerTables.h291
-rw-r--r--utils/TableGen/X86ModRMFilters.h197
-rw-r--r--utils/TableGen/X86RecognizableInstr.cpp959
-rw-r--r--utils/TableGen/X86RecognizableInstr.h237
-rwxr-xr-xutils/buildit/build_llvm6
-rw-r--r--utils/emacs/llvm-mode.el2
-rw-r--r--utils/lit/TestFormats.py11
-rwxr-xr-xutils/lit/lit.py575
-rw-r--r--utils/lit/lit/ExampleTests.ObjDir/lit.site.cfg15
-rw-r--r--utils/lit/lit/ExampleTests/Clang/fsyntax-only.c4
-rw-r--r--utils/lit/lit/ExampleTests/Clang/lit.cfg80
-rw-r--r--utils/lit/lit/ExampleTests/LLVM.InTree/test/Bar/bar-test.ll3
-rw-r--r--utils/lit/lit/ExampleTests/LLVM.InTree/test/Bar/dg.exp6
-rw-r--r--utils/lit/lit/ExampleTests/LLVM.InTree/test/lit.cfg151
-rw-r--r--utils/lit/lit/ExampleTests/LLVM.InTree/test/lit.site.cfg10
-rw-r--r--utils/lit/lit/ExampleTests/LLVM.InTree/test/site.exp30
-rw-r--r--utils/lit/lit/ExampleTests/LLVM.OutOfTree/lit.local.cfg1
-rw-r--r--utils/lit/lit/ExampleTests/LLVM.OutOfTree/obj/test/Foo/lit.local.cfg0
-rw-r--r--utils/lit/lit/ExampleTests/LLVM.OutOfTree/obj/test/lit.site.cfg11
-rw-r--r--utils/lit/lit/ExampleTests/LLVM.OutOfTree/obj/test/site.exp30
-rw-r--r--utils/lit/lit/ExampleTests/LLVM.OutOfTree/src/test/Foo/data.txt1
-rw-r--r--utils/lit/lit/ExampleTests/LLVM.OutOfTree/src/test/Foo/dg.exp6
-rw-r--r--utils/lit/lit/ExampleTests/LLVM.OutOfTree/src/test/Foo/pct-S.ll1
-rw-r--r--utils/lit/lit/ExampleTests/LLVM.OutOfTree/src/test/lit.cfg151
-rw-r--r--utils/lit/lit/ExampleTests/ShExternal/lit.local.cfg6
-rw-r--r--utils/lit/lit/ExampleTests/ShInternal/lit.local.cfg6
-rw-r--r--utils/lit/lit/ExampleTests/TclTest/lit.local.cfg5
-rw-r--r--utils/lit/lit/ExampleTests/TclTest/stderr-pipe.ll1
-rw-r--r--utils/lit/lit/ExampleTests/TclTest/tcl-redir-1.ll7
-rw-r--r--utils/lit/lit/ExampleTests/fail.c2
-rw-r--r--utils/lit/lit/ExampleTests/lit.cfg23
-rw-r--r--utils/lit/lit/ExampleTests/pass.c1
-rw-r--r--utils/lit/lit/ExampleTests/xfail.c2
-rw-r--r--utils/lit/lit/ExampleTests/xpass.c2
-rw-r--r--utils/lit/lit/LitConfig.py95
-rw-r--r--utils/lit/lit/LitFormats.py3
-rw-r--r--utils/lit/lit/ProgressBar.py267
-rw-r--r--utils/lit/lit/ShCommands.py85
-rw-r--r--utils/lit/lit/ShUtil.py346
-rw-r--r--utils/lit/lit/TclUtil.py322
-rw-r--r--utils/lit/lit/Test.py79
-rw-r--r--utils/lit/lit/TestFormats.py189
-rw-r--r--utils/lit/lit/TestRunner.py517
-rw-r--r--utils/lit/lit/TestingConfig.py97
-rw-r--r--utils/lit/lit/Util.py124
-rw-r--r--utils/lit/lit/__init__.py10
-rwxr-xr-xutils/lit/lit/lit.py579
-rw-r--r--utils/lit/setup.py69
-rwxr-xr-xutils/llvmdo1
-rw-r--r--utils/unittest/googletest/gtest.cc2
-rw-r--r--utils/unittest/googletest/include/gtest/gtest-param-test.h7
-rw-r--r--utils/unittest/googletest/tempfile.tmp0
63 files changed, 6146 insertions, 799 deletions
diff --git a/utils/TableGen/CMakeLists.txt b/utils/TableGen/CMakeLists.txt
index daf8676..ce9b66f 100644
--- a/utils/TableGen/CMakeLists.txt
+++ b/utils/TableGen/CMakeLists.txt
@@ -23,6 +23,8 @@ add_executable(tblgen
TGValueTypes.cpp
TableGen.cpp
TableGenBackend.cpp
+ X86DisassemblerTables.cpp
+ X86RecognizableInstr.cpp
)
target_link_libraries(tblgen LLVMSupport LLVMSystem)
diff --git a/utils/TableGen/CodeEmitterGen.cpp b/utils/TableGen/CodeEmitterGen.cpp
index e9f30be..7e6c769 100644
--- a/utils/TableGen/CodeEmitterGen.cpp
+++ b/utils/TableGen/CodeEmitterGen.cpp
@@ -61,11 +61,14 @@ void CodeEmitterGen::reverseBits(std::vector<Record*> &Insts) {
// If the VarBitInit at position 'bit' matches the specified variable then
// return the variable bit position. Otherwise return -1.
-int CodeEmitterGen::getVariableBit(const Init *VarVal,
+int CodeEmitterGen::getVariableBit(const std::string &VarName,
BitsInit *BI, int bit) {
if (VarBitInit *VBI = dynamic_cast<VarBitInit*>(BI->getBit(bit))) {
TypedInit *TI = VBI->getVariable();
- if (TI == VarVal) return VBI->getBitNum();
+
+ if (VarInit *VI = dynamic_cast<VarInit*>(TI)) {
+ if (VI->getName() == VarName) return VBI->getBitNum();
+ }
}
return -1;
@@ -159,11 +162,11 @@ void CodeEmitterGen::run(raw_ostream &o) {
if (!Vals[i].getPrefix() && !Vals[i].getValue()->isComplete()) {
// Is the operand continuous? If so, we can just mask and OR it in
// instead of doing it bit-by-bit, saving a lot in runtime cost.
- const Init *VarVal = Vals[i].getValue();
+ const std::string &VarName = Vals[i].getName();
bool gotOp = false;
for (int bit = BI->getNumBits()-1; bit >= 0; ) {
- int varBit = getVariableBit(VarVal, BI, bit);
+ int varBit = getVariableBit(VarName, BI, bit);
if (varBit == -1) {
--bit;
@@ -173,7 +176,7 @@ void CodeEmitterGen::run(raw_ostream &o) {
int N = 1;
for (--bit; bit >= 0;) {
- varBit = getVariableBit(VarVal, BI, bit);
+ varBit = getVariableBit(VarName, BI, bit);
if (varBit == -1 || varBit != (beginVarBit - N)) break;
++N;
--bit;
@@ -185,7 +188,7 @@ void CodeEmitterGen::run(raw_ostream &o) {
while (CGI.isFlatOperandNotEmitted(op))
++op;
- Case += " // op: " + Vals[i].getName() + "\n"
+ Case += " // op: " + VarName + "\n"
+ " op = getMachineOpValue(MI, MI.getOperand("
+ utostr(op++) + "));\n";
gotOp = true;
diff --git a/utils/TableGen/CodeEmitterGen.h b/utils/TableGen/CodeEmitterGen.h
index 2dc34ba..f0b3229 100644
--- a/utils/TableGen/CodeEmitterGen.h
+++ b/utils/TableGen/CodeEmitterGen.h
@@ -23,7 +23,6 @@ namespace llvm {
class RecordVal;
class BitsInit;
-struct Init;
class CodeEmitterGen : public TableGenBackend {
RecordKeeper &Records;
@@ -36,7 +35,7 @@ private:
void emitMachineOpEmitter(raw_ostream &o, const std::string &Namespace);
void emitGetValueBit(raw_ostream &o, const std::string &Namespace);
void reverseBits(std::vector<Record*> &Insts);
- int getVariableBit(const Init *VarVal, BitsInit *BI, int bit);
+ int getVariableBit(const std::string &VarName, BitsInit *BI, int bit);
};
} // End llvm namespace
diff --git a/utils/TableGen/CodeGenDAGPatterns.cpp b/utils/TableGen/CodeGenDAGPatterns.cpp
index fab41c5..cf79365 100644
--- a/utils/TableGen/CodeGenDAGPatterns.cpp
+++ b/utils/TableGen/CodeGenDAGPatterns.cpp
@@ -321,8 +321,7 @@ bool SDTypeConstraint::ApplyTypeConstraint(TreePatternNode *N,
getOperandNum(x.SDTCisVTSmallerThanOp_Info.OtherOperandNum, N,NumResults);
// It must be integer.
- bool MadeChange = false;
- MadeChange |= OtherNode->UpdateNodeType(MVT::iAny, TP);
+ bool MadeChange = OtherNode->UpdateNodeType(MVT::iAny, TP);
// This code only handles nodes that have one type set. Assert here so
// that we can change this if we ever need to deal with multiple value
@@ -330,7 +329,7 @@ bool SDTypeConstraint::ApplyTypeConstraint(TreePatternNode *N,
assert(OtherNode->getExtTypes().size() == 1 && "Node has too many types!");
if (OtherNode->hasTypeSet() && OtherNode->getTypeNum(0) <= VT)
OtherNode->UpdateNodeType(MVT::Other, TP); // Throw an error.
- return false;
+ return MadeChange;
}
case SDTCisOpSmallerThanOp: {
TreePatternNode *BigOperand =
diff --git a/utils/TableGen/CodeGenInstruction.cpp b/utils/TableGen/CodeGenInstruction.cpp
index 8520d9e..c69ce96 100644
--- a/utils/TableGen/CodeGenInstruction.cpp
+++ b/utils/TableGen/CodeGenInstruction.cpp
@@ -18,36 +18,56 @@
using namespace llvm;
static void ParseConstraint(const std::string &CStr, CodeGenInstruction *I) {
- // FIXME: Only supports TIED_TO for now.
+ // EARLY_CLOBBER: @early $reg
+ std::string::size_type wpos = CStr.find_first_of(" \t");
+ std::string::size_type start = CStr.find_first_not_of(" \t");
+ std::string Tok = CStr.substr(start, wpos - start);
+ if (Tok == "@earlyclobber") {
+ std::string Name = CStr.substr(wpos+1);
+ wpos = Name.find_first_not_of(" \t");
+ if (wpos == std::string::npos)
+ throw "Illegal format for @earlyclobber constraint: '" + CStr + "'";
+ Name = Name.substr(wpos);
+ std::pair<unsigned,unsigned> Op =
+ I->ParseOperandName(Name, false);
+
+ // Build the string for the operand
+ std::string OpConstraint = "(1 << TOI::EARLY_CLOBBER)";
+ if (!I->OperandList[Op.first].Constraints[Op.second].empty())
+ throw "Operand '" + Name + "' cannot have multiple constraints!";
+ I->OperandList[Op.first].Constraints[Op.second] = OpConstraint;
+ return;
+ }
+
+ // Only other constraint is "TIED_TO" for now.
std::string::size_type pos = CStr.find_first_of('=');
assert(pos != std::string::npos && "Unrecognized constraint");
- std::string::size_type start = CStr.find_first_not_of(" \t");
+ start = CStr.find_first_not_of(" \t");
std::string Name = CStr.substr(start, pos - start);
-
+
// TIED_TO: $src1 = $dst
- std::string::size_type wpos = Name.find_first_of(" \t");
+ wpos = Name.find_first_of(" \t");
if (wpos == std::string::npos)
throw "Illegal format for tied-to constraint: '" + CStr + "'";
std::string DestOpName = Name.substr(0, wpos);
std::pair<unsigned,unsigned> DestOp = I->ParseOperandName(DestOpName, false);
-
+
Name = CStr.substr(pos+1);
wpos = Name.find_first_not_of(" \t");
if (wpos == std::string::npos)
throw "Illegal format for tied-to constraint: '" + CStr + "'";
-
+
std::pair<unsigned,unsigned> SrcOp =
I->ParseOperandName(Name.substr(wpos), false);
if (SrcOp > DestOp)
throw "Illegal tied-to operand constraint '" + CStr + "'";
-
-
+
+
unsigned FlatOpNo = I->getFlattenedOperandNumber(SrcOp);
// Build the string for the operand.
std::string OpConstraint =
"((" + utostr(FlatOpNo) + " << 16) | (1 << TOI::TIED_TO))";
-
-
+
if (!I->OperandList[DestOp.first].Constraints[DestOp.second].empty())
throw "Operand '" + DestOpName + "' cannot have multiple constraints!";
I->OperandList[DestOp.first].Constraints[DestOp.second] = OpConstraint;
@@ -56,20 +76,20 @@ static void ParseConstraint(const std::string &CStr, CodeGenInstruction *I) {
static void ParseConstraints(const std::string &CStr, CodeGenInstruction *I) {
// Make sure the constraints list for each operand is large enough to hold
// constraint info, even if none is present.
- for (unsigned i = 0, e = I->OperandList.size(); i != e; ++i)
+ for (unsigned i = 0, e = I->OperandList.size(); i != e; ++i)
I->OperandList[i].Constraints.resize(I->OperandList[i].MINumOperands);
-
+
if (CStr.empty()) return;
-
+
const std::string delims(",");
std::string::size_type bidx, eidx;
-
+
bidx = CStr.find_first_not_of(delims);
while (bidx != std::string::npos) {
eidx = CStr.find_first_of(delims, bidx);
if (eidx == std::string::npos)
eidx = CStr.length();
-
+
ParseConstraint(CStr.substr(bidx, eidx - bidx), I);
bidx = CStr.find_first_not_of(delims, eidx);
}
@@ -145,7 +165,7 @@ CodeGenInstruction::CodeGenInstruction(Record *R, const std::string &AsmStr)
if (Rec->isSubClassOf("Operand")) {
PrintMethod = Rec->getValueAsString("PrintMethod");
MIOpInfo = Rec->getValueAsDag("MIOperandInfo");
-
+
// Verify that MIOpInfo has an 'ops' root value.
if (!dynamic_cast<DefInit*>(MIOpInfo->getOperator()) ||
dynamic_cast<DefInit*>(MIOpInfo->getOperator())
@@ -165,7 +185,7 @@ CodeGenInstruction::CodeGenInstruction(Record *R, const std::string &AsmStr)
} else if (Rec->getName() == "variable_ops") {
isVariadic = true;
continue;
- } else if (!Rec->isSubClassOf("RegisterClass") &&
+ } else if (!Rec->isSubClassOf("RegisterClass") &&
Rec->getName() != "ptr_rc" && Rec->getName() != "unknown")
throw "Unknown operand class '" + Rec->getName() +
"' in '" + R->getName() + "' instruction!";
@@ -177,15 +197,15 @@ CodeGenInstruction::CodeGenInstruction(Record *R, const std::string &AsmStr)
if (!OperandNames.insert(DI->getArgName(i)).second)
throw "In instruction '" + R->getName() + "', operand #" + utostr(i) +
" has the same name as a previous operand!";
-
- OperandList.push_back(OperandInfo(Rec, DI->getArgName(i), PrintMethod,
+
+ OperandList.push_back(OperandInfo(Rec, DI->getArgName(i), PrintMethod,
MIOperandNo, NumOps, MIOpInfo));
MIOperandNo += NumOps;
}
// Parse Constraints.
ParseConstraints(R->getValueAsString("Constraints"), this);
-
+
// For backward compatibility: isTwoAddress means operand 1 is tied to
// operand 0.
if (isTwoAddress) {
@@ -194,13 +214,13 @@ CodeGenInstruction::CodeGenInstruction(Record *R, const std::string &AsmStr)
"already has constraint set!";
OperandList[1].Constraints[0] = "((0 << 16) | (1 << TOI::TIED_TO))";
}
-
+
// Any operands with unset constraints get 0 as their constraint.
for (unsigned op = 0, e = OperandList.size(); op != e; ++op)
for (unsigned j = 0, e = OperandList[op].MINumOperands; j != e; ++j)
if (OperandList[op].Constraints[j].empty())
OperandList[op].Constraints[j] = "0";
-
+
// Parse the DisableEncoding field.
std::string DisableEncoding = R->getValueAsString("DisableEncoding");
while (1) {
@@ -229,15 +249,15 @@ unsigned CodeGenInstruction::getOperandNamed(const std::string &Name) const {
"' does not have an operand named '$" + Name + "'!";
}
-std::pair<unsigned,unsigned>
+std::pair<unsigned,unsigned>
CodeGenInstruction::ParseOperandName(const std::string &Op,
bool AllowWholeOp) {
if (Op.empty() || Op[0] != '$')
throw TheDef->getName() + ": Illegal operand name: '" + Op + "'";
-
+
std::string OpName = Op.substr(1);
std::string SubOpName;
-
+
// Check to see if this is $foo.bar.
std::string::size_type DotIdx = OpName.find_first_of(".");
if (DotIdx != std::string::npos) {
@@ -246,7 +266,7 @@ CodeGenInstruction::ParseOperandName(const std::string &Op,
throw TheDef->getName() + ": illegal empty suboperand name in '" +Op +"'";
OpName = OpName.substr(0, DotIdx);
}
-
+
unsigned OpIdx = getOperandNamed(OpName);
if (SubOpName.empty()) { // If no suboperand name was specified:
@@ -255,16 +275,16 @@ CodeGenInstruction::ParseOperandName(const std::string &Op,
SubOpName.empty())
throw TheDef->getName() + ": Illegal to refer to"
" whole operand part of complex operand '" + Op + "'";
-
+
// Otherwise, return the operand.
return std::make_pair(OpIdx, 0U);
}
-
+
// Find the suboperand number involved.
DagInit *MIOpInfo = OperandList[OpIdx].MIOperandInfo;
if (MIOpInfo == 0)
throw TheDef->getName() + ": unknown suboperand name in '" + Op + "'";
-
+
// Find the operand with the right name.
for (unsigned i = 0, e = MIOpInfo->getNumArgs(); i != e; ++i)
if (MIOpInfo->getArgName(i) == SubOpName)
diff --git a/utils/TableGen/DAGISelEmitter.cpp b/utils/TableGen/DAGISelEmitter.cpp
index 66debe2..a901fd0 100644
--- a/utils/TableGen/DAGISelEmitter.cpp
+++ b/utils/TableGen/DAGISelEmitter.cpp
@@ -1292,8 +1292,8 @@ public:
// possible and it avoids CSE map recalculation for the node's
// users, however it's tricky to use in a non-root context.
//
- // We also don't use if the pattern replacement is being used to
- // jettison a chain result, since morphing the node in place
+ // We also don't use SelectNodeTo if the pattern replacement is being
+ // used to jettison a chain result, since morphing the node in place
// would leave users of the chain dangling.
//
if (!isRoot || (InputHasChain && !NodeHasChain)) {
diff --git a/utils/TableGen/DisassemblerEmitter.cpp b/utils/TableGen/DisassemblerEmitter.cpp
index cc13125..61b9b15 100644
--- a/utils/TableGen/DisassemblerEmitter.cpp
+++ b/utils/TableGen/DisassemblerEmitter.cpp
@@ -10,7 +10,86 @@
#include "DisassemblerEmitter.h"
#include "CodeGenTarget.h"
#include "Record.h"
+#include "X86DisassemblerTables.h"
+#include "X86RecognizableInstr.h"
using namespace llvm;
+using namespace llvm::X86Disassembler;
+
+/// DisassemblerEmitter - Contains disassembler table emitters for various
+/// architectures.
+
+/// X86 Disassembler Emitter
+///
+/// *** IF YOU'RE HERE TO RESOLVE A "Primary decode conflict", LOOK DOWN NEAR
+/// THE END OF THIS COMMENT!
+///
+/// The X86 disassembler emitter is part of the X86 Disassembler, which is
+/// documented in lib/Target/X86/X86Disassembler.h.
+///
+/// The emitter produces the tables that the disassembler uses to translate
+/// instructions. The emitter generates the following tables:
+///
+/// - One table (CONTEXTS_SYM) that contains a mapping of attribute masks to
+/// instruction contexts. Although for each attribute there are cases where
+/// that attribute determines decoding, in the majority of cases decoding is
+/// the same whether or not an attribute is present. For example, a 64-bit
+/// instruction with an OPSIZE prefix and an XS prefix decodes the same way in
+/// all cases as a 64-bit instruction with only OPSIZE set. (The XS prefix
+/// may have effects on its execution, but does not change the instruction
+/// returned.) This allows considerable space savings in other tables.
+/// - Four tables (ONEBYTE_SYM, TWOBYTE_SYM, THREEBYTE38_SYM, and
+/// THREEBYTE3A_SYM) contain the hierarchy that the decoder traverses while
+/// decoding an instruction. At the lowest level of this hierarchy are
+/// instruction UIDs, 16-bit integers that can be used to uniquely identify
+/// the instruction and correspond exactly to its position in the list of
+/// CodeGenInstructions for the target.
+/// - One table (INSTRUCTIONS_SYM) contains information about the operands of
+/// each instruction and how to decode them.
+///
+/// During table generation, there may be conflicts between instructions that
+/// occupy the same space in the decode tables. These conflicts are resolved as
+/// follows in setTableFields() (X86DisassemblerTables.cpp)
+///
+/// - If the current context is the native context for one of the instructions
+/// (that is, the attributes specified for it in the LLVM tables specify
+/// precisely the current context), then it has priority.
+/// - If the current context isn't native for either of the instructions, then
+/// the higher-priority context wins (that is, the one that is more specific).
+/// That hierarchy is determined by outranks() (X86DisassemblerTables.cpp)
+/// - If the current context is native for both instructions, then the table
+/// emitter reports a conflict and dies.
+///
+/// *** RESOLUTION FOR "Primary decode conflict"S
+///
+/// If two instructions collide, typically the solution is (in order of
+/// likelihood):
+///
+/// (1) to filter out one of the instructions by editing filter()
+/// (X86RecognizableInstr.cpp). This is the most common resolution, but
+/// check the Intel manuals first to make sure that (2) and (3) are not the
+/// problem.
+/// (2) to fix the tables (X86.td and its subsidiaries) so the opcodes are
+/// accurate. Sometimes they are not.
+/// (3) to fix the tables to reflect the actual context (for example, required
+/// prefixes), and possibly to add a new context by editing
+/// lib/Target/X86/X86DisassemblerDecoderCommon.h. This is unlikely to be
+/// the cause.
+///
+/// DisassemblerEmitter.cpp contains the implementation for the emitter,
+/// which simply pulls out instructions from the CodeGenTarget and pushes them
+/// into X86DisassemblerTables.
+/// X86DisassemblerTables.h contains the interface for the instruction tables,
+/// which manage and emit the structures discussed above.
+/// X86DisassemblerTables.cpp contains the implementation for the instruction
+/// tables.
+/// X86ModRMFilters.h contains filters that can be used to determine which
+/// ModR/M values are valid for a particular instruction. These are used to
+/// populate ModRMDecisions.
+/// X86RecognizableInstr.h contains the interface for a single instruction,
+/// which knows how to translate itself from a CodeGenInstruction and provide
+/// the information necessary for integration into the tables.
+/// X86RecognizableInstr.cpp contains the implementation for a single
+/// instruction.
void DisassemblerEmitter::run(raw_ostream &OS) {
CodeGenTarget Target;
@@ -25,6 +104,26 @@ void DisassemblerEmitter::run(raw_ostream &OS) {
<< " *===---------------------------------------------------------------"
<< "-------===*/\n";
+ // X86 uses a custom disassembler.
+ if (Target.getName() == "X86") {
+ DisassemblerTables Tables;
+
+ std::vector<const CodeGenInstruction*> numberedInstructions;
+ Target.getInstructionsByEnumValue(numberedInstructions);
+
+ for (unsigned i = 0, e = numberedInstructions.size(); i != e; ++i)
+ RecognizableInstr::processInstr(Tables, *numberedInstructions[i], i);
+
+ // FIXME: As long as we are using exceptions, might as well drop this to the
+ // actual conflict site.
+ if (Tables.hasConflicts())
+ throw TGError(Target.getTargetRecord()->getLoc(),
+ "Primary decode conflict");
+
+ Tables.emit(OS);
+ return;
+ }
+
throw TGError(Target.getTargetRecord()->getLoc(),
"Unable to generate disassembler for this target");
}
diff --git a/utils/TableGen/LLVMCConfigurationEmitter.cpp b/utils/TableGen/LLVMCConfigurationEmitter.cpp
index 5be9ab7..88fb6c3 100644
--- a/utils/TableGen/LLVMCConfigurationEmitter.cpp
+++ b/utils/TableGen/LLVMCConfigurationEmitter.cpp
@@ -17,6 +17,7 @@
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringSet.h"
+
#include <algorithm>
#include <cassert>
#include <functional>
@@ -26,6 +27,7 @@
using namespace llvm;
+namespace {
//===----------------------------------------------------------------------===//
/// Typedefs
@@ -37,18 +39,16 @@ typedef std::vector<std::string> StrVector;
/// Constants
// Indentation.
-static const unsigned TabWidth = 4;
-static const unsigned Indent1 = TabWidth*1;
-static const unsigned Indent2 = TabWidth*2;
-static const unsigned Indent3 = TabWidth*3;
+const unsigned TabWidth = 4;
+const unsigned Indent1 = TabWidth*1;
+const unsigned Indent2 = TabWidth*2;
+const unsigned Indent3 = TabWidth*3;
// Default help string.
-static const char * const DefaultHelpString = "NO HELP MESSAGE PROVIDED";
+const char * const DefaultHelpString = "NO HELP MESSAGE PROVIDED";
// Name for the "sink" option.
-static const char * const SinkOptionName = "AutoGeneratedSinkOption";
-
-namespace {
+const char * const SinkOptionName = "AutoGeneratedSinkOption";
//===----------------------------------------------------------------------===//
/// Helper functions
@@ -86,26 +86,30 @@ const DagInit& InitPtrToDag(const Init* ptr) {
return val;
}
-const std::string GetOperatorName(const DagInit* D) {
- return D->getOperator()->getAsString();
+const std::string GetOperatorName(const DagInit& D) {
+ return D.getOperator()->getAsString();
}
-const std::string GetOperatorName(const DagInit& D) {
- return GetOperatorName(&D);
+/// CheckBooleanConstant - Check that the provided value is a boolean constant.
+void CheckBooleanConstant(const Init* I) {
+ const DefInit& val = dynamic_cast<const DefInit&>(*I);
+ const std::string& str = val.getAsString();
+
+ if (str != "true" && str != "false") {
+ throw "Incorrect boolean value: '" + str +
+ "': must be either 'true' or 'false'";
+ }
}
-// checkNumberOfArguments - Ensure that the number of args in d is
+// CheckNumberOfArguments - Ensure that the number of args in d is
// greater than or equal to min_arguments, otherwise throw an exception.
-void checkNumberOfArguments (const DagInit* d, unsigned minArgs) {
- if (!d || d->getNumArgs() < minArgs)
+void CheckNumberOfArguments (const DagInit& d, unsigned minArgs) {
+ if (d.getNumArgs() < minArgs)
throw GetOperatorName(d) + ": too few arguments!";
}
-void checkNumberOfArguments (const DagInit& d, unsigned minArgs) {
- checkNumberOfArguments(&d, minArgs);
-}
-// isDagEmpty - is this DAG marked with an empty marker?
-bool isDagEmpty (const DagInit* d) {
+// IsDagEmpty - is this DAG marked with an empty marker?
+bool IsDagEmpty (const DagInit& d) {
return GetOperatorName(d) == "empty_dag_marker";
}
@@ -132,8 +136,8 @@ std::string EscapeVariableName(const std::string& Var) {
return ret;
}
-/// oneOf - Does the input string contain this character?
-bool oneOf(const char* lst, char c) {
+/// OneOf - Does the input string contain this character?
+bool OneOf(const char* lst, char c) {
while (*lst) {
if (*lst++ == c)
return true;
@@ -142,7 +146,7 @@ bool oneOf(const char* lst, char c) {
}
template <class I, class S>
-void checkedIncrement(I& P, I E, S ErrorString) {
+void CheckedIncrement(I& P, I E, S ErrorString) {
++P;
if (P == E)
throw ErrorString;
@@ -499,17 +503,36 @@ public:
};
+template <class Handler, class FunctionObject>
+Handler GetHandler(FunctionObject* Obj, const DagInit& Dag) {
+ const std::string& HandlerName = GetOperatorName(Dag);
+ return Obj->GetHandler(HandlerName);
+}
+
+template <class FunctionObject>
+void InvokeDagInitHandler(FunctionObject* Obj, Init* I) {
+ typedef void (FunctionObject::*Handler) (const DagInit&);
+
+ const DagInit& Dag = InitPtrToDag(I);
+ Handler h = GetHandler<Handler>(Obj, Dag);
+
+ ((Obj)->*(h))(Dag);
+}
+
template <class FunctionObject>
-void InvokeDagInitHandler(FunctionObject* Obj, Init* i) {
- typedef void (FunctionObject::*Handler) (const DagInit*);
+void InvokeDagInitHandler(const FunctionObject* const Obj,
+ const Init* I, unsigned IndentLevel, raw_ostream& O)
+{
+ typedef void (FunctionObject::*Handler)
+ (const DagInit&, unsigned IndentLevel, raw_ostream& O) const;
- const DagInit& property = InitPtrToDag(i);
- const std::string& property_name = GetOperatorName(property);
- Handler h = Obj->GetHandler(property_name);
+ const DagInit& Dag = InitPtrToDag(I);
+ Handler h = GetHandler<Handler>(Obj, Dag);
- ((Obj)->*(h))(&property);
+ ((Obj)->*(h))(Dag, IndentLevel, O);
}
+
template <typename H>
typename HandlerTable<H>::HandlerMap HandlerTable<H>::Handlers_;
@@ -521,7 +544,7 @@ bool HandlerTable<H>::staticMembersInitialized_ = false;
/// option property list.
class CollectOptionProperties;
typedef void (CollectOptionProperties::* CollectOptionPropertiesHandler)
-(const DagInit*);
+(const DagInit&);
class CollectOptionProperties
: public HandlerTable<CollectOptionPropertiesHandler>
@@ -555,8 +578,8 @@ public:
/// operator() - Just forwards to the corresponding property
/// handler.
- void operator() (Init* i) {
- InvokeDagInitHandler(this, i);
+ void operator() (Init* I) {
+ InvokeDagInitHandler(this, I);
}
private:
@@ -564,44 +587,44 @@ private:
/// Option property handlers --
/// Methods that handle option properties such as (help) or (hidden).
- void onExtern (const DagInit* d) {
- checkNumberOfArguments(d, 0);
+ void onExtern (const DagInit& d) {
+ CheckNumberOfArguments(d, 0);
optDesc_.setExtern();
}
- void onHelp (const DagInit* d) {
- checkNumberOfArguments(d, 1);
- optDesc_.Help = InitPtrToString(d->getArg(0));
+ void onHelp (const DagInit& d) {
+ CheckNumberOfArguments(d, 1);
+ optDesc_.Help = InitPtrToString(d.getArg(0));
}
- void onHidden (const DagInit* d) {
- checkNumberOfArguments(d, 0);
+ void onHidden (const DagInit& d) {
+ CheckNumberOfArguments(d, 0);
optDesc_.setHidden();
}
- void onReallyHidden (const DagInit* d) {
- checkNumberOfArguments(d, 0);
+ void onReallyHidden (const DagInit& d) {
+ CheckNumberOfArguments(d, 0);
optDesc_.setReallyHidden();
}
- void onCommaSeparated (const DagInit* d) {
- checkNumberOfArguments(d, 0);
+ void onCommaSeparated (const DagInit& d) {
+ CheckNumberOfArguments(d, 0);
if (!optDesc_.isList())
throw "'comma_separated' is valid only on list options!";
optDesc_.setCommaSeparated();
}
- void onRequired (const DagInit* d) {
- checkNumberOfArguments(d, 0);
+ void onRequired (const DagInit& d) {
+ CheckNumberOfArguments(d, 0);
if (optDesc_.isOneOrMore() || optDesc_.isOptional())
throw "Only one of (required), (optional) or "
"(one_or_more) properties is allowed!";
optDesc_.setRequired();
}
- void onInit (const DagInit* d) {
- checkNumberOfArguments(d, 1);
- Init* i = d->getArg(0);
+ void onInit (const DagInit& d) {
+ CheckNumberOfArguments(d, 1);
+ Init* i = d.getArg(0);
const std::string& str = i->getAsString();
bool correct = optDesc_.isParameter() && dynamic_cast<StringInit*>(i);
@@ -613,8 +636,8 @@ private:
optDesc_.InitVal = i;
}
- void onOneOrMore (const DagInit* d) {
- checkNumberOfArguments(d, 0);
+ void onOneOrMore (const DagInit& d) {
+ CheckNumberOfArguments(d, 0);
if (optDesc_.isRequired() || optDesc_.isOptional())
throw "Only one of (required), (optional) or "
"(one_or_more) properties is allowed!";
@@ -624,8 +647,8 @@ private:
optDesc_.setOneOrMore();
}
- void onOptional (const DagInit* d) {
- checkNumberOfArguments(d, 0);
+ void onOptional (const DagInit& d) {
+ CheckNumberOfArguments(d, 0);
if (optDesc_.isRequired() || optDesc_.isOneOrMore())
throw "Only one of (required), (optional) or "
"(one_or_more) properties is allowed!";
@@ -635,9 +658,9 @@ private:
optDesc_.setOptional();
}
- void onMultiVal (const DagInit* d) {
- checkNumberOfArguments(d, 1);
- int val = InitPtrToInt(d->getArg(0));
+ void onMultiVal (const DagInit& d) {
+ CheckNumberOfArguments(d, 1);
+ int val = InitPtrToInt(d.getArg(0));
if (val < 2)
throw "Error in the 'multi_val' property: "
"the value must be greater than 1!";
@@ -660,7 +683,7 @@ public:
void operator()(const Init* i) {
const DagInit& d = InitPtrToDag(i);
- checkNumberOfArguments(&d, 1);
+ CheckNumberOfArguments(d, 1);
const OptionType::OptionType Type =
stringToOptionType(GetOperatorName(d));
@@ -669,14 +692,14 @@ public:
OptionDescription OD(Type, Name);
if (!OD.isExtern())
- checkNumberOfArguments(&d, 2);
+ CheckNumberOfArguments(d, 2);
if (OD.isAlias()) {
// Aliases store the aliased option name in the 'Help' field.
OD.Help = InitPtrToString(d.getArg(1));
}
else if (!OD.isExtern()) {
- processOptionProperties(&d, OD);
+ processOptionProperties(d, OD);
}
OptDescs_.InsertDescription(OD);
}
@@ -684,12 +707,12 @@ public:
private:
/// processOptionProperties - Go through the list of option
/// properties and call a corresponding handler for each.
- static void processOptionProperties (const DagInit* d, OptionDescription& o) {
- checkNumberOfArguments(d, 2);
- DagInit::const_arg_iterator B = d->arg_begin();
+ static void processOptionProperties (const DagInit& d, OptionDescription& o) {
+ CheckNumberOfArguments(d, 2);
+ DagInit::const_arg_iterator B = d.arg_begin();
// Skip the first argument: it's always the option name.
++B;
- std::for_each(B, d->arg_end(), CollectOptionProperties(o));
+ std::for_each(B, d.arg_end(), CollectOptionProperties(o));
}
};
@@ -750,7 +773,7 @@ typedef std::vector<IntrusiveRefCntPtr<ToolDescription> > ToolDescriptions;
class CollectToolProperties;
typedef void (CollectToolProperties::* CollectToolPropertiesHandler)
-(const DagInit*);
+(const DagInit&);
class CollectToolProperties : public HandlerTable<CollectToolPropertiesHandler>
{
@@ -779,8 +802,8 @@ public:
}
}
- void operator() (Init* i) {
- InvokeDagInitHandler(this, i);
+ void operator() (Init* I) {
+ InvokeDagInitHandler(this, I);
}
private:
@@ -789,23 +812,23 @@ private:
/// Functions that extract information about tool properties from
/// DAG representation.
- void onActions (const DagInit* d) {
- checkNumberOfArguments(d, 1);
- Init* Case = d->getArg(0);
+ void onActions (const DagInit& d) {
+ CheckNumberOfArguments(d, 1);
+ Init* Case = d.getArg(0);
if (typeid(*Case) != typeid(DagInit) ||
- GetOperatorName(static_cast<DagInit*>(Case)) != "case")
+ GetOperatorName(static_cast<DagInit&>(*Case)) != "case")
throw "The argument to (actions) should be a 'case' construct!";
toolDesc_.Actions = Case;
}
- void onCmdLine (const DagInit* d) {
- checkNumberOfArguments(d, 1);
- toolDesc_.CmdLine = d->getArg(0);
+ void onCmdLine (const DagInit& d) {
+ CheckNumberOfArguments(d, 1);
+ toolDesc_.CmdLine = d.getArg(0);
}
- void onInLanguage (const DagInit* d) {
- checkNumberOfArguments(d, 1);
- Init* arg = d->getArg(0);
+ void onInLanguage (const DagInit& d) {
+ CheckNumberOfArguments(d, 1);
+ Init* arg = d.getArg(0);
// Find out the argument's type.
if (typeid(*arg) == typeid(StringInit)) {
@@ -830,23 +853,23 @@ private:
}
}
- void onJoin (const DagInit* d) {
- checkNumberOfArguments(d, 0);
+ void onJoin (const DagInit& d) {
+ CheckNumberOfArguments(d, 0);
toolDesc_.setJoin();
}
- void onOutLanguage (const DagInit* d) {
- checkNumberOfArguments(d, 1);
- toolDesc_.OutLanguage = InitPtrToString(d->getArg(0));
+ void onOutLanguage (const DagInit& d) {
+ CheckNumberOfArguments(d, 1);
+ toolDesc_.OutLanguage = InitPtrToString(d.getArg(0));
}
- void onOutputSuffix (const DagInit* d) {
- checkNumberOfArguments(d, 1);
- toolDesc_.OutputSuffix = InitPtrToString(d->getArg(0));
+ void onOutputSuffix (const DagInit& d) {
+ CheckNumberOfArguments(d, 1);
+ toolDesc_.OutputSuffix = InitPtrToString(d.getArg(0));
}
- void onSink (const DagInit* d) {
- checkNumberOfArguments(d, 0);
+ void onSink (const DagInit& d) {
+ CheckNumberOfArguments(d, 0);
toolDesc_.setSink();
}
@@ -1033,12 +1056,12 @@ void WalkCase(const Init* Case, F1 TestCallback, F2 StatementCallback,
throw "Case construct handler: no corresponding action "
"found for the test " + Test.getAsString() + '!';
- TestCallback(&Test, IndentLevel, (i == 1));
+ TestCallback(Test, IndentLevel, (i == 1));
}
else
{
if (dynamic_cast<DagInit*>(arg)
- && GetOperatorName(static_cast<DagInit*>(arg)) == "case") {
+ && GetOperatorName(static_cast<DagInit&>(*arg)) == "case") {
// Nested 'case'.
WalkCase(arg, TestCallback, StatementCallback, IndentLevel + Indent1);
}
@@ -1063,14 +1086,28 @@ class ExtractOptionNames {
if (ActionName == "forward" || ActionName == "forward_as" ||
ActionName == "forward_value" ||
ActionName == "forward_transformed_value" ||
- ActionName == "switch_on" || ActionName == "parameter_equals" ||
+ ActionName == "switch_on" || ActionName == "any_switch_on" ||
+ ActionName == "parameter_equals" ||
ActionName == "element_in_list" || ActionName == "not_empty" ||
ActionName == "empty") {
- checkNumberOfArguments(&Stmt, 1);
- const std::string& Name = InitPtrToString(Stmt.getArg(0));
- OptionNames_.insert(Name);
+ CheckNumberOfArguments(Stmt, 1);
+
+ Init* Arg = Stmt.getArg(0);
+ if (typeid(*Arg) == typeid(StringInit)) {
+ const std::string& Name = InitPtrToString(Arg);
+ OptionNames_.insert(Name);
+ }
+ else {
+ // It's a list.
+ const ListInit& List = InitPtrToList(Arg);
+ for (ListInit::const_iterator B = List.begin(), E = List.end();
+ B != E; ++B) {
+ const std::string& Name = InitPtrToString(*B);
+ OptionNames_.insert(Name);
+ }
+ }
}
- else if (ActionName == "and" || ActionName == "or") {
+ else if (ActionName == "and" || ActionName == "or" || ActionName == "not") {
for (unsigned i = 0, NumArgs = Stmt.getNumArgs(); i < NumArgs; ++i) {
this->processDag(Stmt.getArg(i));
}
@@ -1093,8 +1130,8 @@ public:
}
}
- void operator()(const DagInit* Test, unsigned, bool) {
- this->operator()(Test);
+ void operator()(const DagInit& Test, unsigned, bool) {
+ this->operator()(&Test);
}
void operator()(const Init* Statement, unsigned) {
this->operator()(Statement);
@@ -1125,10 +1162,10 @@ void CheckForSuperfluousOptions (const RecordVector& Edges,
for (RecordVector::const_iterator B = Edges.begin(), E = Edges.end();
B != E; ++B) {
const Record* Edge = *B;
- DagInit* Weight = Edge->getValueAsDag("weight");
+ DagInit& Weight = *Edge->getValueAsDag("weight");
- if (!isDagEmpty(Weight))
- WalkCase(Weight, ExtractOptionNames(nonSuperfluousOptions), Id());
+ if (!IsDagEmpty(Weight))
+ WalkCase(&Weight, ExtractOptionNames(nonSuperfluousOptions), Id());
}
// Check that all options in OptDescs belong to the set of
@@ -1169,7 +1206,7 @@ void EmitListTest(const ListInit& L, const char* LogicOp,
if (isFirst)
isFirst = false;
else
- O << " || ";
+ O << ' ' << LogicOp << ' ';
Callback(InitPtrToString(*B), O);
}
}
@@ -1217,7 +1254,7 @@ bool EmitCaseTest1ArgList(const std::string& TestName,
const DagInit& d,
const OptionDescriptions& OptDescs,
raw_ostream& O) {
- const ListInit& L = *static_cast<ListInit*>(d.getArg(0));
+ const ListInit& L = InitPtrToList(d.getArg(0));
if (TestName == "any_switch_on") {
EmitListTest(L, "||", EmitSwitchOn(OptDescs), O);
@@ -1284,7 +1321,7 @@ bool EmitCaseTest1Arg(const std::string& TestName,
const DagInit& d,
const OptionDescriptions& OptDescs,
raw_ostream& O) {
- checkNumberOfArguments(&d, 1);
+ CheckNumberOfArguments(d, 1);
if (typeid(*d.getArg(0)) == typeid(ListInit))
return EmitCaseTest1ArgList(TestName, d, OptDescs, O);
else
@@ -1297,7 +1334,7 @@ bool EmitCaseTest2Args(const std::string& TestName,
unsigned IndentLevel,
const OptionDescriptions& OptDescs,
raw_ostream& O) {
- checkNumberOfArguments(&d, 2);
+ CheckNumberOfArguments(d, 2);
const std::string& OptName = InitPtrToString(d.getArg(0));
const std::string& OptArg = InitPtrToString(d.getArg(1));
@@ -1348,7 +1385,7 @@ void EmitLogicalOperationTest(const DagInit& d, const char* LogicOp,
void EmitLogicalNot(const DagInit& d, unsigned IndentLevel,
const OptionDescriptions& OptDescs, raw_ostream& O)
{
- checkNumberOfArguments(&d, 1);
+ CheckNumberOfArguments(d, 1);
const DagInit& InnerTest = InitPtrToDag(d.getArg(0));
O << "! (";
EmitCaseTest(InnerTest, IndentLevel, OptDescs, O);
@@ -1374,7 +1411,7 @@ void EmitCaseTest(const DagInit& d, unsigned IndentLevel,
else if (EmitCaseTest2Args(TestName, d, IndentLevel, OptDescs, O))
return;
else
- throw TestName + ": unknown edge property!";
+ throw "Unknown test '" + TestName + "' used in the 'case' construct!";
}
@@ -1390,7 +1427,7 @@ public:
: EmitElseIf_(EmitElseIf), OptDescs_(OptDescs), O_(O)
{}
- void operator()(const DagInit* Test, unsigned IndentLevel, bool FirstTest)
+ void operator()(const DagInit& Test, unsigned IndentLevel, bool FirstTest)
{
if (GetOperatorName(Test) == "default") {
O_.indent(IndentLevel) << "else {\n";
@@ -1398,7 +1435,7 @@ public:
else {
O_.indent(IndentLevel)
<< ((!FirstTest && EmitElseIf_) ? "else if (" : "if (");
- EmitCaseTest(*Test, IndentLevel, OptDescs_, O_);
+ EmitCaseTest(Test, IndentLevel, OptDescs_, O_);
O_ << ") {\n";
}
}
@@ -1419,7 +1456,7 @@ public:
// Ignore nested 'case' DAG.
if (!(dynamic_cast<const DagInit*>(Statement) &&
- GetOperatorName(static_cast<const DagInit*>(Statement)) == "case")) {
+ GetOperatorName(static_cast<const DagInit&>(*Statement)) == "case")) {
if (typeid(*Statement) == typeid(ListInit)) {
const ListInit& DagList = *static_cast<const ListInit*>(Statement);
for (ListInit::const_iterator B = DagList.begin(), E = DagList.end();
@@ -1452,10 +1489,10 @@ void EmitCaseConstructHandler(const Init* Case, unsigned IndentLevel,
EmitCaseStatementCallback<F>(Callback, O), IndentLevel);
}
-/// TokenizeCmdline - converts from
+/// TokenizeCmdLine - converts from
/// "$CALL(HookName, 'Arg1', 'Arg2')/path -arg1 -arg2" to
/// ["$CALL(", "HookName", "Arg1", "Arg2", ")/path", "-arg1", "-arg2"].
-void TokenizeCmdline(const std::string& CmdLine, StrVector& Out) {
+void TokenizeCmdLine(const std::string& CmdLine, StrVector& Out) {
const char* Delimiters = " \t\n\v\f\r";
enum TokenizerState
{ Normal, SpecialCommand, InsideSpecialCommand, InsideQuotationMarks }
@@ -1477,7 +1514,7 @@ void TokenizeCmdline(const std::string& CmdLine, StrVector& Out) {
cur_st = SpecialCommand;
break;
}
- if (oneOf(Delimiters, cur_ch)) {
+ if (OneOf(Delimiters, cur_ch)) {
// Skip whitespace
B = CmdLine.find_first_not_of(Delimiters, B);
if (B == std::string::npos) {
@@ -1492,7 +1529,7 @@ void TokenizeCmdline(const std::string& CmdLine, StrVector& Out) {
case SpecialCommand:
- if (oneOf(Delimiters, cur_ch)) {
+ if (OneOf(Delimiters, cur_ch)) {
cur_st = Normal;
Out.push_back("");
continue;
@@ -1505,7 +1542,7 @@ void TokenizeCmdline(const std::string& CmdLine, StrVector& Out) {
break;
case InsideSpecialCommand:
- if (oneOf(Delimiters, cur_ch)) {
+ if (OneOf(Delimiters, cur_ch)) {
continue;
}
if (cur_ch == '\'') {
@@ -1544,7 +1581,7 @@ SubstituteCall (StrVector::const_iterator Pos,
bool IsJoin, raw_ostream& O)
{
const char* errorMessage = "Syntax error in $CALL invocation!";
- checkedIncrement(Pos, End, errorMessage);
+ CheckedIncrement(Pos, End, errorMessage);
const std::string& CmdName = *Pos;
if (CmdName == ")")
@@ -1556,7 +1593,7 @@ SubstituteCall (StrVector::const_iterator Pos,
bool firstIteration = true;
while (true) {
- checkedIncrement(Pos, End, errorMessage);
+ CheckedIncrement(Pos, End, errorMessage);
const std::string& Arg = *Pos;
assert(Arg.size() != 0);
@@ -1591,7 +1628,7 @@ SubstituteEnv (StrVector::const_iterator Pos,
StrVector::const_iterator End, raw_ostream& O)
{
const char* errorMessage = "Syntax error in $ENV invocation!";
- checkedIncrement(Pos, End, errorMessage);
+ CheckedIncrement(Pos, End, errorMessage);
const std::string& EnvName = *Pos;
if (EnvName == ")")
@@ -1601,7 +1638,7 @@ SubstituteEnv (StrVector::const_iterator Pos,
O << EnvName;
O << "\"))";
- checkedIncrement(Pos, End, errorMessage);
+ CheckedIncrement(Pos, End, errorMessage);
return Pos;
}
@@ -1642,7 +1679,7 @@ void EmitCmdLineVecFill(const Init* CmdLine, const std::string& ToolName,
bool IsJoin, unsigned IndentLevel,
raw_ostream& O) {
StrVector StrVec;
- TokenizeCmdline(InitPtrToString(CmdLine), StrVec);
+ TokenizeCmdLine(InitPtrToString(CmdLine), StrVec);
if (StrVec.empty())
throw "Tool '" + ToolName + "' has empty command line!";
@@ -1786,7 +1823,8 @@ void EmitForwardOptionPropertyHandlingCode (const OptionDescription& D,
/// ActionHandlingCallbackBase - Base class of EmitActionHandlersCallback and
/// EmitPreprocessOptionsCallback.
-struct ActionHandlingCallbackBase {
+struct ActionHandlingCallbackBase
+{
void onErrorDag(const DagInit& d,
unsigned IndentLevel, raw_ostream& O) const
@@ -1801,7 +1839,7 @@ struct ActionHandlingCallbackBase {
void onWarningDag(const DagInit& d,
unsigned IndentLevel, raw_ostream& O) const
{
- checkNumberOfArguments(&d, 1);
+ CheckNumberOfArguments(d, 1);
O.indent(IndentLevel) << "llvm::errs() << \""
<< InitPtrToString(d.getArg(0)) << "\";\n";
}
@@ -1810,17 +1848,20 @@ struct ActionHandlingCallbackBase {
/// EmitActionHandlersCallback - Emit code that handles actions. Used by
/// EmitGenerateActionMethod() as an argument to EmitCaseConstructHandler().
+
class EmitActionHandlersCallback;
+
typedef void (EmitActionHandlersCallback::* EmitActionHandlersCallbackHandler)
(const DagInit&, unsigned, raw_ostream&) const;
-class EmitActionHandlersCallback
-: public ActionHandlingCallbackBase,
+class EmitActionHandlersCallback :
+ public ActionHandlingCallbackBase,
public HandlerTable<EmitActionHandlersCallbackHandler>
{
- const OptionDescriptions& OptDescs;
typedef EmitActionHandlersCallbackHandler Handler;
+ const OptionDescriptions& OptDescs;
+
/// EmitHookInvocation - Common code for hook invocation from actions. Used by
/// onAppendCmd and onOutputSuffix.
void EmitHookInvocation(const std::string& Str,
@@ -1828,7 +1869,7 @@ class EmitActionHandlersCallback
unsigned IndentLevel, raw_ostream& O) const
{
StrVector Out;
- TokenizeCmdline(Str, Out);
+ TokenizeCmdLine(Str, Out);
for (StrVector::const_iterator B = Out.begin(), E = Out.end();
B != E; ++B) {
@@ -1848,7 +1889,7 @@ class EmitActionHandlersCallback
void onAppendCmd (const DagInit& Dag,
unsigned IndentLevel, raw_ostream& O) const
{
- checkNumberOfArguments(&Dag, 1);
+ CheckNumberOfArguments(Dag, 1);
this->EmitHookInvocation(InitPtrToString(Dag.getArg(0)),
"vec.push_back(", ");\n", IndentLevel, O);
}
@@ -1856,7 +1897,7 @@ class EmitActionHandlersCallback
void onForward (const DagInit& Dag,
unsigned IndentLevel, raw_ostream& O) const
{
- checkNumberOfArguments(&Dag, 1);
+ CheckNumberOfArguments(Dag, 1);
const std::string& Name = InitPtrToString(Dag.getArg(0));
EmitForwardOptionPropertyHandlingCode(OptDescs.FindOption(Name),
IndentLevel, "", O);
@@ -1865,7 +1906,7 @@ class EmitActionHandlersCallback
void onForwardAs (const DagInit& Dag,
unsigned IndentLevel, raw_ostream& O) const
{
- checkNumberOfArguments(&Dag, 2);
+ CheckNumberOfArguments(Dag, 2);
const std::string& Name = InitPtrToString(Dag.getArg(0));
const std::string& NewName = InitPtrToString(Dag.getArg(1));
EmitForwardOptionPropertyHandlingCode(OptDescs.FindOption(Name),
@@ -1875,7 +1916,7 @@ class EmitActionHandlersCallback
void onForwardValue (const DagInit& Dag,
unsigned IndentLevel, raw_ostream& O) const
{
- checkNumberOfArguments(&Dag, 1);
+ CheckNumberOfArguments(Dag, 1);
const std::string& Name = InitPtrToString(Dag.getArg(0));
const OptionDescription& D = OptDescs.FindListOrParameter(Name);
@@ -1893,7 +1934,7 @@ class EmitActionHandlersCallback
void onForwardTransformedValue (const DagInit& Dag,
unsigned IndentLevel, raw_ostream& O) const
{
- checkNumberOfArguments(&Dag, 2);
+ CheckNumberOfArguments(Dag, 2);
const std::string& Name = InitPtrToString(Dag.getArg(0));
const std::string& Hook = InitPtrToString(Dag.getArg(1));
const OptionDescription& D = OptDescs.FindListOrParameter(Name);
@@ -1906,7 +1947,7 @@ class EmitActionHandlersCallback
void onOutputSuffix (const DagInit& Dag,
unsigned IndentLevel, raw_ostream& O) const
{
- checkNumberOfArguments(&Dag, 1);
+ CheckNumberOfArguments(Dag, 1);
this->EmitHookInvocation(InitPtrToString(Dag.getArg(0)),
"output_suffix = ", ";\n", IndentLevel, O);
}
@@ -1949,20 +1990,16 @@ class EmitActionHandlersCallback
}
}
- void operator()(const Init* Statement,
+ void operator()(const Init* I,
unsigned IndentLevel, raw_ostream& O) const
{
- const DagInit& Dag = InitPtrToDag(Statement);
- const std::string& ActionName = GetOperatorName(Dag);
- Handler h = GetHandler(ActionName);
-
- ((this)->*(h))(Dag, IndentLevel, O);
+ InvokeDagInitHandler(this, I, IndentLevel, O);
}
};
bool IsOutFileIndexCheckRequiredStr (const Init* CmdLine) {
StrVector StrVec;
- TokenizeCmdline(InitPtrToString(CmdLine), StrVec);
+ TokenizeCmdLine(InitPtrToString(CmdLine), StrVec);
for (StrVector::const_iterator I = StrVec.begin(), E = StrVec.end();
I != E; ++I) {
@@ -2280,11 +2317,46 @@ void EmitOptionDefinitions (const OptionDescriptions& descs,
/// EmitPreprocessOptionsCallback - Helper function passed to
/// EmitCaseConstructHandler() by EmitPreprocessOptions().
-class EmitPreprocessOptionsCallback : ActionHandlingCallbackBase {
+
+class EmitPreprocessOptionsCallback;
+
+typedef void
+(EmitPreprocessOptionsCallback::* EmitPreprocessOptionsCallbackHandler)
+(const DagInit&, unsigned, raw_ostream&) const;
+
+class EmitPreprocessOptionsCallback :
+ public ActionHandlingCallbackBase,
+ public HandlerTable<EmitPreprocessOptionsCallbackHandler>
+{
+ typedef EmitPreprocessOptionsCallbackHandler Handler;
+ typedef void
+ (EmitPreprocessOptionsCallback::* HandlerImpl)
+ (const Init*, unsigned, raw_ostream&) const;
+
const OptionDescriptions& OptDescs_;
- void onUnsetOption(Init* i, unsigned IndentLevel, raw_ostream& O) {
- const std::string& OptName = InitPtrToString(i);
+ void onListOrDag(const DagInit& d, HandlerImpl h,
+ unsigned IndentLevel, raw_ostream& O) const
+ {
+ CheckNumberOfArguments(d, 1);
+ const Init* I = d.getArg(0);
+
+ // If I is a list, apply h to each element.
+ if (typeid(*I) == typeid(ListInit)) {
+ const ListInit& L = *static_cast<const ListInit*>(I);
+ for (ListInit::const_iterator B = L.begin(), E = L.end(); B != E; ++B)
+ ((this)->*(h))(*B, IndentLevel, O);
+ }
+ // Otherwise, apply h to I.
+ else {
+ ((this)->*(h))(I, IndentLevel, O);
+ }
+ }
+
+ void onUnsetOptionImpl(const Init* I,
+ unsigned IndentLevel, raw_ostream& O) const
+ {
+ const std::string& OptName = InitPtrToString(I);
const OptionDescription& OptDesc = OptDescs_.FindOption(OptName);
if (OptDesc.isSwitch()) {
@@ -2301,45 +2373,93 @@ class EmitPreprocessOptionsCallback : ActionHandlingCallbackBase {
}
}
- void processDag(const Init* I, unsigned IndentLevel, raw_ostream& O)
+ void onUnsetOption(const DagInit& d,
+ unsigned IndentLevel, raw_ostream& O) const
{
- const DagInit& d = InitPtrToDag(I);
- const std::string& OpName = GetOperatorName(d);
+ this->onListOrDag(d, &EmitPreprocessOptionsCallback::onUnsetOptionImpl,
+ IndentLevel, O);
+ }
+
+ void onSetOptionImpl(const DagInit& d,
+ unsigned IndentLevel, raw_ostream& O) const {
+ CheckNumberOfArguments(d, 2);
+ const std::string& OptName = InitPtrToString(d.getArg(0));
+ const Init* Value = d.getArg(1);
+ const OptionDescription& OptDesc = OptDescs_.FindOption(OptName);
- if (OpName == "warning") {
- this->onWarningDag(d, IndentLevel, O);
+ if (OptDesc.isList()) {
+ const ListInit& List = InitPtrToList(Value);
+
+ O.indent(IndentLevel) << OptDesc.GenVariableName() << ".clear();\n";
+ for (ListInit::const_iterator B = List.begin(), E = List.end();
+ B != E; ++B) {
+ O.indent(IndentLevel) << OptDesc.GenVariableName() << ".push_back(\""
+ << InitPtrToString(*B) << "\");\n";
+ }
}
- else if (OpName == "error") {
- this->onWarningDag(d, IndentLevel, O);
+ else if (OptDesc.isSwitch()) {
+ CheckBooleanConstant(Value);
+ O.indent(IndentLevel) << OptDesc.GenVariableName()
+ << " = " << Value->getAsString() << ";\n";
}
- else if (OpName == "unset_option") {
- checkNumberOfArguments(&d, 1);
- Init* I = d.getArg(0);
- if (typeid(*I) == typeid(ListInit)) {
- const ListInit& DagList = *static_cast<const ListInit*>(I);
- for (ListInit::const_iterator B = DagList.begin(), E = DagList.end();
- B != E; ++B)
- this->onUnsetOption(*B, IndentLevel, O);
- }
- else {
- this->onUnsetOption(I, IndentLevel, O);
- }
+ else if (OptDesc.isParameter()) {
+ const std::string& Str = InitPtrToString(Value);
+ O.indent(IndentLevel) << OptDesc.GenVariableName()
+ << " = \"" << Str << "\";\n";
}
else {
- throw "Unknown operator in the option preprocessor: '" + OpName + "'!"
- "\nOnly 'warning', 'error' and 'unset_option' are allowed.";
+ throw "Can't apply 'set_option' to alias option -" + OptName + " !";
}
}
-public:
+ void onSetSwitch(const Init* I,
+ unsigned IndentLevel, raw_ostream& O) const {
+ const std::string& OptName = InitPtrToString(I);
+ const OptionDescription& OptDesc = OptDescs_.FindOption(OptName);
- void operator()(const Init* I, unsigned IndentLevel, raw_ostream& O) {
- this->processDag(I, IndentLevel, O);
+ if (OptDesc.isSwitch())
+ O.indent(IndentLevel) << OptDesc.GenVariableName() << " = true;\n";
+ else
+ throw "set_option: -" + OptName + " is not a switch option!";
+ }
+
+ void onSetOption(const DagInit& d,
+ unsigned IndentLevel, raw_ostream& O) const
+ {
+ CheckNumberOfArguments(d, 1);
+
+ // Two arguments: (set_option "parameter", VALUE), where VALUE can be a
+ // boolean, a string or a string list.
+ if (d.getNumArgs() > 1)
+ this->onSetOptionImpl(d, IndentLevel, O);
+ // One argument: (set_option "switch")
+ // or (set_option ["switch1", "switch2", ...])
+ else
+ this->onListOrDag(d, &EmitPreprocessOptionsCallback::onSetSwitch,
+ IndentLevel, O);
}
+public:
+
EmitPreprocessOptionsCallback(const OptionDescriptions& OptDescs)
: OptDescs_(OptDescs)
- {}
+ {
+ if (!staticMembersInitialized_) {
+ AddHandler("error", &EmitPreprocessOptionsCallback::onErrorDag);
+ AddHandler("warning", &EmitPreprocessOptionsCallback::onWarningDag);
+ AddHandler("unset_option", &EmitPreprocessOptionsCallback::onUnsetOption);
+ AddHandler("set_option", &EmitPreprocessOptionsCallback::onSetOption);
+
+ staticMembersInitialized_ = true;
+ }
+ }
+
+ void operator()(const Init* I,
+ unsigned IndentLevel, raw_ostream& O) const
+ {
+ InvokeDagInitHandler(this, I, IndentLevel, O);
+ }
+
};
/// EmitPreprocessOptions - Emit the PreprocessOptionsLocal() function.
@@ -2407,7 +2527,7 @@ void IncDecWeight (const Init* i, unsigned IndentLevel,
O.indent(IndentLevel) << "ret -= ";
}
else if (OpName == "error") {
- checkNumberOfArguments(&d, 1);
+ CheckNumberOfArguments(d, 1);
O.indent(IndentLevel) << "throw std::runtime_error(\""
<< InitPtrToString(d.getArg(0))
<< "\");\n";
@@ -2445,7 +2565,7 @@ void EmitEdgeClass (unsigned N, const std::string& Target,
EmitCaseConstructHandler(Case, Indent2, IncDecWeight, false, OptDescs, O);
O.indent(Indent2) << "return ret;\n";
- O.indent(Indent1) << "};\n\n};\n\n";
+ O.indent(Indent1) << "}\n\n};\n\n";
}
/// EmitEdgeClasses - Emit Edge* classes that represent graph edges.
@@ -2457,10 +2577,10 @@ void EmitEdgeClasses (const RecordVector& EdgeVector,
E = EdgeVector.end(); B != E; ++B) {
const Record* Edge = *B;
const std::string& NodeB = Edge->getValueAsString("b");
- DagInit* Weight = Edge->getValueAsDag("weight");
+ DagInit& Weight = *Edge->getValueAsDag("weight");
- if (!isDagEmpty(Weight))
- EmitEdgeClass(i, NodeB, Weight, OptDescs, O);
+ if (!IsDagEmpty(Weight))
+ EmitEdgeClass(i, NodeB, &Weight, OptDescs, O);
++i;
}
}
@@ -2487,11 +2607,11 @@ void EmitPopulateCompilationGraph (const RecordVector& EdgeVector,
const Record* Edge = *B;
const std::string& NodeA = Edge->getValueAsString("a");
const std::string& NodeB = Edge->getValueAsString("b");
- DagInit* Weight = Edge->getValueAsDag("weight");
+ DagInit& Weight = *Edge->getValueAsDag("weight");
O.indent(Indent1) << "G.insertEdge(\"" << NodeA << "\", ";
- if (isDagEmpty(Weight))
+ if (IsDagEmpty(Weight))
O << "new SimpleEdge(\"" << NodeB << "\")";
else
O << "new Edge" << i << "()";
@@ -2540,7 +2660,7 @@ public:
const std::string& Name = GetOperatorName(Dag);
if (Name == "forward_transformed_value") {
- checkNumberOfArguments(Dag, 2);
+ CheckNumberOfArguments(Dag, 2);
const std::string& OptName = InitPtrToString(Dag.getArg(0));
const std::string& HookName = InitPtrToString(Dag.getArg(1));
const OptionDescription& D = OptDescs_.FindOption(OptName);
@@ -2549,14 +2669,14 @@ public:
: HookInfo::ArgHook);
}
else if (Name == "append_cmd" || Name == "output_suffix") {
- checkNumberOfArguments(Dag, 1);
+ CheckNumberOfArguments(Dag, 1);
this->onCmdLine(InitPtrToString(Dag.getArg(0)));
}
}
void onCmdLine(const std::string& Cmd) {
StrVector cmds;
- TokenizeCmdline(Cmd, cmds);
+ TokenizeCmdLine(Cmd, cmds);
for (StrVector::const_iterator B = cmds.begin(), E = cmds.end();
B != E; ++B) {
@@ -2564,7 +2684,7 @@ public:
if (cmd == "$CALL") {
unsigned NumArgs = 0;
- checkedIncrement(B, E, "Syntax error in $CALL invocation!");
+ CheckedIncrement(B, E, "Syntax error in $CALL invocation!");
const std::string& HookName = *B;
if (HookName.at(0) == ')')
@@ -2789,7 +2909,6 @@ void CheckPluginData(PluginData& Data) {
// Check that there are no options without side effects (specified
// only in the OptionList).
CheckForSuperfluousOptions(Data.Edges, Data.ToolDescs, Data.OptDescs);
-
}
void EmitPluginCode(const PluginData& Data, raw_ostream& O) {
diff --git a/utils/TableGen/Record.cpp b/utils/TableGen/Record.cpp
index 53f9014..542735e 100644
--- a/utils/TableGen/Record.cpp
+++ b/utils/TableGen/Record.cpp
@@ -945,11 +945,13 @@ Init *TernOpInit::Fold(Record *CurRec, MultiClass *CurMultiClass) {
std::string Val = RHSs->getValue();
std::string::size_type found;
+ std::string::size_type idx = 0;
do {
- found = Val.find(LHSs->getValue());
+ found = Val.find(LHSs->getValue(), idx);
if (found != std::string::npos) {
Val.replace(found, LHSs->getValue().size(), MHSs->getValue());
}
+ idx = found + MHSs->getValue().size();
} while (found != std::string::npos);
return new StringInit(Val);
diff --git a/utils/TableGen/X86DisassemblerShared.h b/utils/TableGen/X86DisassemblerShared.h
new file mode 100644
index 0000000..0417e9d
--- /dev/null
+++ b/utils/TableGen/X86DisassemblerShared.h
@@ -0,0 +1,38 @@
+//===- X86DisassemblerShared.h - Emitter shared header ----------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef X86DISASSEMBLERSHARED_H
+#define X86DISASSEMBLERSHARED_H
+
+#include <string>
+#include <string.h>
+
+#define INSTRUCTION_SPECIFIER_FIELDS \
+ bool filtered; \
+ InstructionContext insnContext; \
+ std::string name; \
+ \
+ InstructionSpecifier() { \
+ filtered = false; \
+ insnContext = IC; \
+ name = ""; \
+ modifierType = MODIFIER_NONE; \
+ modifierBase = 0; \
+ memset(operands, 0, sizeof(operands)); \
+ }
+
+#define INSTRUCTION_IDS \
+ InstrUID instructionIDs[256];
+
+#include "../../lib/Target/X86/Disassembler/X86DisassemblerDecoderCommon.h"
+
+#undef INSTRUCTION_SPECIFIER_FIELDS
+#undef INSTRUCTION_IDS
+
+#endif
diff --git a/utils/TableGen/X86DisassemblerTables.cpp b/utils/TableGen/X86DisassemblerTables.cpp
new file mode 100644
index 0000000..be07031
--- /dev/null
+++ b/utils/TableGen/X86DisassemblerTables.cpp
@@ -0,0 +1,603 @@
+//===- X86DisassemblerTables.cpp - Disassembler tables ----------*- 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 X86 Disassembler Emitter.
+// It contains the implementation of the disassembler tables.
+// Documentation for the disassembler emitter in general can be found in
+// X86DisasemblerEmitter.h.
+//
+//===----------------------------------------------------------------------===//
+
+#include "X86DisassemblerShared.h"
+#include "X86DisassemblerTables.h"
+
+#include "TableGenBackend.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/Format.h"
+
+using namespace llvm;
+using namespace X86Disassembler;
+
+/// inheritsFrom - Indicates whether all instructions in one class also belong
+/// to another class.
+///
+/// @param child - The class that may be the subset
+/// @param parent - The class that may be the superset
+/// @return - True if child is a subset of parent, false otherwise.
+static inline bool inheritsFrom(InstructionContext child,
+ InstructionContext parent) {
+ if (child == parent)
+ return true;
+
+ switch (parent) {
+ case IC:
+ return true;
+ case IC_64BIT:
+ return(inheritsFrom(child, IC_64BIT_REXW) ||
+ inheritsFrom(child, IC_64BIT_OPSIZE) ||
+ inheritsFrom(child, IC_64BIT_XD) ||
+ inheritsFrom(child, IC_64BIT_XS));
+ case IC_OPSIZE:
+ return(inheritsFrom(child, IC_64BIT_OPSIZE));
+ case IC_XD:
+ return(inheritsFrom(child, IC_64BIT_XD));
+ case IC_XS:
+ return(inheritsFrom(child, IC_64BIT_XS));
+ case IC_64BIT_REXW:
+ return(inheritsFrom(child, IC_64BIT_REXW_XS) ||
+ inheritsFrom(child, IC_64BIT_REXW_XD) ||
+ inheritsFrom(child, IC_64BIT_REXW_OPSIZE));
+ case IC_64BIT_OPSIZE:
+ return(inheritsFrom(child, IC_64BIT_REXW_OPSIZE));
+ case IC_64BIT_XD:
+ return(inheritsFrom(child, IC_64BIT_REXW_XD));
+ case IC_64BIT_XS:
+ return(inheritsFrom(child, IC_64BIT_REXW_XS));
+ case IC_64BIT_REXW_XD:
+ return false;
+ case IC_64BIT_REXW_XS:
+ return false;
+ case IC_64BIT_REXW_OPSIZE:
+ return false;
+ default:
+ return false;
+ }
+}
+
+/// outranks - Indicates whether, if an instruction has two different applicable
+/// classes, which class should be preferred when performing decode. This
+/// imposes a total ordering (ties are resolved toward "lower")
+///
+/// @param upper - The class that may be preferable
+/// @param lower - The class that may be less preferable
+/// @return - True if upper is to be preferred, false otherwise.
+static inline bool outranks(InstructionContext upper,
+ InstructionContext lower) {
+ assert(upper < IC_max);
+ assert(lower < IC_max);
+
+#define ENUM_ENTRY(n, r, d) r,
+ static int ranks[IC_max] = {
+ INSTRUCTION_CONTEXTS
+ };
+#undef ENUM_ENTRY
+
+ return (ranks[upper] > ranks[lower]);
+}
+
+/// stringForContext - Returns a string containing the name of a particular
+/// InstructionContext, usually for diagnostic purposes.
+///
+/// @param insnContext - The instruction class to transform to a string.
+/// @return - A statically-allocated string constant that contains the
+/// name of the instruction class.
+static inline const char* stringForContext(InstructionContext insnContext) {
+ switch (insnContext) {
+ default:
+ llvm_unreachable("Unhandled instruction class");
+#define ENUM_ENTRY(n, r, d) case n: return #n; break;
+ INSTRUCTION_CONTEXTS
+#undef ENUM_ENTRY
+ }
+
+ return 0;
+}
+
+/// stringForOperandType - Like stringForContext, but for OperandTypes.
+static inline const char* stringForOperandType(OperandType type) {
+ switch (type) {
+ default:
+ llvm_unreachable("Unhandled type");
+#define ENUM_ENTRY(i, d) case i: return #i;
+ TYPES
+#undef ENUM_ENTRY
+ }
+}
+
+/// stringForOperandEncoding - like stringForContext, but for
+/// OperandEncodings.
+static inline const char* stringForOperandEncoding(OperandEncoding encoding) {
+ switch (encoding) {
+ default:
+ llvm_unreachable("Unhandled encoding");
+#define ENUM_ENTRY(i, d) case i: return #i;
+ ENCODINGS
+#undef ENUM_ENTRY
+ }
+}
+
+void DisassemblerTables::emitOneID(raw_ostream &o,
+ uint32_t &i,
+ InstrUID id,
+ bool addComma) const {
+ if (id)
+ o.indent(i * 2) << format("0x%hx", id);
+ else
+ o.indent(i * 2) << 0;
+
+ if (addComma)
+ o << ", ";
+ else
+ o << " ";
+
+ o << "/* ";
+ o << InstructionSpecifiers[id].name;
+ o << "*/";
+
+ o << "\n";
+}
+
+/// emitEmptyTable - Emits the modRMEmptyTable, which is used as a ID table by
+/// all ModR/M decisions for instructions that are invalid for all possible
+/// ModR/M byte values.
+///
+/// @param o - The output stream on which to emit the table.
+/// @param i - The indentation level for that output stream.
+static void emitEmptyTable(raw_ostream &o, uint32_t &i)
+{
+ o.indent(i * 2) << "InstrUID modRMEmptyTable[1] = { 0 };" << "\n";
+ o << "\n";
+}
+
+/// getDecisionType - Determines whether a ModRM decision with 255 entries can
+/// be compacted by eliminating redundant information.
+///
+/// @param decision - The decision to be compacted.
+/// @return - The compactest available representation for the decision.
+static ModRMDecisionType getDecisionType(ModRMDecision &decision)
+{
+ bool satisfiesOneEntry = true;
+ bool satisfiesSplitRM = 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 (satisfiesOneEntry)
+ return MODRM_ONEENTRY;
+
+ if (satisfiesSplitRM)
+ return MODRM_SPLITRM;
+
+ return MODRM_FULL;
+}
+
+/// stringForDecisionType - Returns a statically-allocated string corresponding
+/// to a particular decision type.
+///
+/// @param dt - The decision type.
+/// @return - A pointer to the statically-allocated string (e.g.,
+/// "MODRM_ONEENTRY" for MODRM_ONEENTRY).
+static const char* stringForDecisionType(ModRMDecisionType dt)
+{
+#define ENUM_ENTRY(n) case n: return #n;
+ switch (dt) {
+ default:
+ llvm_unreachable("Unknown decision type");
+ MODRMTYPES
+ };
+#undef ENUM_ENTRY
+}
+
+/// stringForModifierType - Returns a statically-allocated string corresponding
+/// to an opcode modifier type.
+///
+/// @param mt - The modifier type.
+/// @return - A pointer to the statically-allocated string (e.g.,
+/// "MODIFIER_NONE" for MODIFIER_NONE).
+static const char* stringForModifierType(ModifierType mt)
+{
+#define ENUM_ENTRY(n) case n: return #n;
+ switch(mt) {
+ default:
+ llvm_unreachable("Unknown modifier type");
+ MODIFIER_TYPES
+ };
+#undef ENUM_ENTRY
+}
+
+DisassemblerTables::DisassemblerTables() {
+ unsigned i;
+
+ for (i = 0; i < 4; i++) {
+ Tables[i] = new ContextDecision;
+ memset(Tables[i], 0, sizeof(ContextDecision));
+ }
+
+ HasConflicts = false;
+}
+
+DisassemblerTables::~DisassemblerTables() {
+ unsigned i;
+
+ for (i = 0; i < 4; i++)
+ delete Tables[i];
+}
+
+void DisassemblerTables::emitModRMDecision(raw_ostream &o1,
+ raw_ostream &o2,
+ uint32_t &i1,
+ uint32_t &i2,
+ ModRMDecision &decision)
+ const {
+ static uint64_t sTableNumber = 0;
+ uint64_t thisTableNumber = sTableNumber;
+ 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";
+
+ i2--;
+ o2.indent(i2) << "}";
+ return;
+ }
+
+ o1.indent(i1) << "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";
+ i1++;
+
+ switch (dt) {
+ default:
+ llvm_unreachable("Unknown decision type");
+ case MODRM_ONEENTRY:
+ emitOneID(o1, i1, decision.instructionIDs[0], false);
+ break;
+ case MODRM_SPLITRM:
+ emitOneID(o1, i1, decision.instructionIDs[0x00], true); // mod = 0b00
+ emitOneID(o1, i1, decision.instructionIDs[0xc0], false); // mod = 0b11
+ break;
+ case MODRM_FULL:
+ for (index = 0; index < 256; ++index)
+ emitOneID(o1, i1, decision.instructionIDs[index], index < 255);
+ 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";
+
+ i2--;
+ o2.indent(i2) << "}";
+
+ ++sTableNumber;
+}
+
+void DisassemblerTables::emitOpcodeDecision(
+ raw_ostream &o1,
+ raw_ostream &o2,
+ uint32_t &i1,
+ uint32_t &i2,
+ OpcodeDecision &decision) const {
+ uint16_t index;
+
+ o2.indent(i2) << "{ /* struct OpcodeDecision */" << "\n";
+ i2++;
+ o2.indent(i2) << "{" << "\n";
+ i2++;
+
+ for (index = 0; index < 256; ++index) {
+ o2.indent(i2);
+
+ o2 << "/* 0x" << format("%02hhx", index) << " */" << "\n";
+
+ emitModRMDecision(o1, o2, i1, i2, decision.modRMDecisions[index]);
+
+ if (index < 255)
+ o2 << ",";
+
+ o2 << "\n";
+ }
+
+ i2--;
+ o2.indent(i2) << "}" << "\n";
+ i2--;
+ o2.indent(i2) << "}" << "\n";
+}
+
+void DisassemblerTables::emitContextDecision(
+ raw_ostream &o1,
+ raw_ostream &o2,
+ uint32_t &i1,
+ uint32_t &i2,
+ ContextDecision &decision,
+ const char* name) const {
+ o2.indent(i2) << "struct ContextDecision " << name << " = {" << "\n";
+ i2++;
+ o2.indent(i2) << "{ /* opcodeDecisions */" << "\n";
+ i2++;
+
+ unsigned index;
+
+ for (index = 0; index < IC_max; ++index) {
+ o2.indent(i2) << "/* ";
+ o2 << stringForContext((InstructionContext)index);
+ o2 << " */";
+ o2 << "\n";
+
+ emitOpcodeDecision(o1, o2, i1, i2, decision.opcodeDecisions[index]);
+
+ if (index + 1 < IC_max)
+ o2 << ", ";
+ }
+
+ i2--;
+ o2.indent(i2) << "}" << "\n";
+ i2--;
+ o2.indent(i2) << "};" << "\n";
+}
+
+void DisassemblerTables::emitInstructionInfo(raw_ostream &o, uint32_t &i)
+ const {
+ o.indent(i * 2) << "struct InstructionSpecifier ";
+ o << INSTRUCTIONS_STR << "[";
+ o << InstructionSpecifiers.size();
+ o << "] = {" << "\n";
+
+ i++;
+
+ uint16_t numInstructions = InstructionSpecifiers.size();
+ uint16_t index, operandIndex;
+
+ for (index = 0; index < numInstructions; ++index) {
+ o.indent(i * 2) << "{ /* " << index << " */" << "\n";
+ i++;
+
+ o.indent(i * 2) <<
+ stringForModifierType(InstructionSpecifiers[index].modifierType);
+ o << "," << "\n";
+
+ o.indent(i * 2) << "0x";
+ o << format("%02hhx", (uint16_t)InstructionSpecifiers[index].modifierBase);
+ o << "," << "\n";
+
+ o.indent(i * 2) << "{" << "\n";
+ i++;
+
+ for (operandIndex = 0; operandIndex < X86_MAX_OPERANDS; ++operandIndex) {
+ o.indent(i * 2) << "{ ";
+ o << stringForOperandEncoding(InstructionSpecifiers[index]
+ .operands[operandIndex]
+ .encoding);
+ o << ", ";
+ o << stringForOperandType(InstructionSpecifiers[index]
+ .operands[operandIndex]
+ .type);
+ o << " }";
+
+ if (operandIndex < X86_MAX_OPERANDS - 1)
+ o << ",";
+
+ o << "\n";
+ }
+
+ i--;
+ o.indent(i * 2) << "}," << "\n";
+
+ o.indent(i * 2) << "\"" << InstructionSpecifiers[index].name << "\"";
+ o << "\n";
+
+ i--;
+ o.indent(i * 2) << "}";
+
+ if (index + 1 < numInstructions)
+ o << ",";
+
+ o << "\n";
+ }
+
+ i--;
+ o.indent(i * 2) << "};" << "\n";
+}
+
+void DisassemblerTables::emitContextTable(raw_ostream &o, uint32_t &i) const {
+ uint16_t index;
+
+ o.indent(i * 2) << "InstructionContext ";
+ o << CONTEXTS_STR << "[256] = {" << "\n";
+ i++;
+
+ for (index = 0; index < 256; ++index) {
+ o.indent(i * 2);
+
+ if ((index & ATTR_64BIT) && (index & ATTR_REXW) && (index & ATTR_XS))
+ o << "IC_64BIT_REXW_XS";
+ else if ((index & ATTR_64BIT) && (index & ATTR_REXW) && (index & ATTR_XD))
+ o << "IC_64BIT_REXW_XD";
+ else if ((index & ATTR_64BIT) && (index & ATTR_REXW) &&
+ (index & ATTR_OPSIZE))
+ o << "IC_64BIT_REXW_OPSIZE";
+ else if ((index & ATTR_64BIT) && (index & ATTR_XS))
+ o << "IC_64BIT_XS";
+ else if ((index & ATTR_64BIT) && (index & ATTR_XD))
+ o << "IC_64BIT_XD";
+ else if ((index & ATTR_64BIT) && (index & ATTR_OPSIZE))
+ o << "IC_64BIT_OPSIZE";
+ else if ((index & ATTR_64BIT) && (index & ATTR_REXW))
+ o << "IC_64BIT_REXW";
+ else if ((index & ATTR_64BIT))
+ o << "IC_64BIT";
+ else if (index & ATTR_XS)
+ o << "IC_XS";
+ else if (index & ATTR_XD)
+ o << "IC_XD";
+ else if (index & ATTR_OPSIZE)
+ o << "IC_OPSIZE";
+ else
+ o << "IC";
+
+ if (index < 255)
+ o << ",";
+ else
+ o << " ";
+
+ o << " /* " << index << " */";
+
+ o << "\n";
+ }
+
+ i--;
+ o.indent(i * 2) << "};" << "\n";
+}
+
+void DisassemblerTables::emitContextDecisions(raw_ostream &o1,
+ raw_ostream &o2,
+ uint32_t &i1,
+ uint32_t &i2)
+ const {
+ emitContextDecision(o1, o2, i1, i2, *Tables[0], ONEBYTE_STR);
+ emitContextDecision(o1, o2, i1, i2, *Tables[1], TWOBYTE_STR);
+ emitContextDecision(o1, o2, i1, i2, *Tables[2], THREEBYTE38_STR);
+ emitContextDecision(o1, o2, i1, i2, *Tables[3], THREEBYTE3A_STR);
+}
+
+void DisassemblerTables::emit(raw_ostream &o) const {
+ uint32_t i1 = 0;
+ uint32_t i2 = 0;
+
+ std::string s1;
+ std::string s2;
+
+ raw_string_ostream o1(s1);
+ raw_string_ostream o2(s2);
+
+ emitInstructionInfo(o, i2);
+ o << "\n";
+
+ emitContextTable(o, i2);
+ o << "\n";
+
+ emitEmptyTable(o1, i1);
+ emitContextDecisions(o1, o2, i1, i2);
+
+ o << o1.str();
+ o << "\n";
+ o << o2.str();
+ o << "\n";
+ o << "\n";
+}
+
+void DisassemblerTables::setTableFields(ModRMDecision &decision,
+ const ModRMFilter &filter,
+ InstrUID uid,
+ uint8_t opcode) {
+ unsigned index;
+
+ for (index = 0; index < 256; ++index) {
+ if (filter.accepts(index)) {
+ if (decision.instructionIDs[index] == uid)
+ continue;
+
+ if (decision.instructionIDs[index] != 0) {
+ InstructionSpecifier &newInfo =
+ InstructionSpecifiers[uid];
+ InstructionSpecifier &previousInfo =
+ InstructionSpecifiers[decision.instructionIDs[index]];
+
+ if(newInfo.filtered)
+ continue; // filtered instructions get lowest priority
+
+ if(previousInfo.name == "NOOP")
+ continue; // special case for XCHG32ar and NOOP
+
+ if (outranks(previousInfo.insnContext, newInfo.insnContext))
+ continue;
+
+ if (previousInfo.insnContext == newInfo.insnContext &&
+ !previousInfo.filtered) {
+ errs() << "Error: Primary decode conflict: ";
+ errs() << newInfo.name << " would overwrite " << previousInfo.name;
+ errs() << "\n";
+ errs() << "ModRM " << index << "\n";
+ errs() << "Opcode " << (uint16_t)opcode << "\n";
+ errs() << "Context " << stringForContext(newInfo.insnContext) << "\n";
+ HasConflicts = true;
+ }
+ }
+
+ decision.instructionIDs[index] = uid;
+ }
+ }
+}
+
+void DisassemblerTables::setTableFields(OpcodeType type,
+ InstructionContext insnContext,
+ uint8_t opcode,
+ const ModRMFilter &filter,
+ InstrUID uid) {
+ unsigned index;
+
+ ContextDecision &decision = *Tables[type];
+
+ for (index = 0; index < IC_max; ++index) {
+ if (inheritsFrom((InstructionContext)index,
+ InstructionSpecifiers[uid].insnContext))
+ setTableFields(decision.opcodeDecisions[index].modRMDecisions[opcode],
+ filter,
+ uid,
+ opcode);
+ }
+}
diff --git a/utils/TableGen/X86DisassemblerTables.h b/utils/TableGen/X86DisassemblerTables.h
new file mode 100644
index 0000000..08eba01
--- /dev/null
+++ b/utils/TableGen/X86DisassemblerTables.h
@@ -0,0 +1,291 @@
+//===- X86DisassemblerTables.h - Disassembler tables ------------*- 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 X86 Disassembler Emitter.
+// It contains the interface of the disassembler tables.
+// Documentation for the disassembler emitter in general can be found in
+// X86DisasemblerEmitter.h.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef X86DISASSEMBLERTABLES_H
+#define X86DISASSEMBLERTABLES_H
+
+#include "X86DisassemblerShared.h"
+#include "X86ModRMFilters.h"
+
+#include "llvm/Support/raw_ostream.h"
+
+#include <vector>
+
+namespace llvm {
+
+namespace X86Disassembler {
+
+/// DisassemblerTables - Encapsulates all the decode tables being generated by
+/// the table emitter. Contains functions to populate the tables as well as
+/// to emit them as hierarchical C structures suitable for consumption by the
+/// runtime.
+class DisassemblerTables {
+private:
+ /// The decoder tables. There is one for each opcode type:
+ /// [0] one-byte opcodes
+ /// [1] two-byte opcodes of the form 0f __
+ /// [2] three-byte opcodes of the form 0f 38 __
+ /// [3] three-byte opcodes of the form 0f 3a __
+ ContextDecision* Tables[4];
+
+ /// The instruction information table
+ std::vector<InstructionSpecifier> InstructionSpecifiers;
+
+ /// True if there are primary decode conflicts in the instruction set
+ bool HasConflicts;
+
+ /// emitOneID - Emits a table entry for a single instruction entry, at the
+ /// innermost level of the structure hierarchy. The entry is printed out
+ /// in the format "nnnn, /* MNEMONIC */" where nnnn is the ID in decimal,
+ /// the comma is printed if addComma is true, and the menonic is the name
+ /// of the instruction as listed in the LLVM tables.
+ ///
+ /// @param o - The output stream to print the entry on.
+ /// @param i - The indentation level for o.
+ /// @param id - The unique ID of the instruction to print.
+ /// @param addComma - Whether or not to print a comma after the ID. True if
+ /// additional items will follow.
+ void emitOneID(raw_ostream &o,
+ uint32_t &i,
+ InstrUID id,
+ bool addComma) const;
+
+ /// emitModRMDecision - Emits a table of entries corresponding to a single
+ /// ModR/M decision. Compacts the ModR/M decision if possible. ModR/M
+ /// decisions are printed as:
+ ///
+ /// { /* struct ModRMDecision */
+ /// TYPE,
+ /// modRMTablennnn
+ /// }
+ ///
+ /// where nnnn is a unique ID for the corresponding table of IDs.
+ /// TYPE indicates whether the table has one entry that is the same
+ /// regardless of ModR/M byte, two entries - one for bytes 0x00-0xbf and one
+ /// for bytes 0xc0-0xff -, or 256 entries, one for each possible byte.
+ /// nnnn is the number of a table for looking up these values. The tables
+ /// are writen separately so that tables consisting entirely of zeros will
+ /// not be duplicated. (These all have the name modRMEmptyTable.) A table
+ /// is printed as:
+ ///
+ /// InstrUID modRMTablennnn[k] = {
+ /// nnnn, /* MNEMONIC */
+ /// ...
+ /// nnnn /* MNEMONIC */
+ /// };
+ ///
+ /// @param o1 - The output stream to print the ID table to.
+ /// @param o2 - The output stream to print the decision structure to.
+ /// @param i1 - The indentation level to use with stream o1.
+ /// @param i2 - The indentation level to use with stream o2.
+ /// @param decision - The ModR/M decision to emit. This decision has 256
+ /// entries - emitModRMDecision decides how to compact it.
+ void emitModRMDecision(raw_ostream &o1,
+ raw_ostream &o2,
+ uint32_t &i1,
+ uint32_t &i2,
+ ModRMDecision &decision) const;
+
+ /// emitOpcodeDecision - Emits an OpcodeDecision and all its subsidiary ModR/M
+ /// decisions. An OpcodeDecision is printed as:
+ ///
+ /// { /* struct OpcodeDecision */
+ /// /* 0x00 */
+ /// { /* struct ModRMDecision */
+ /// ...
+ /// }
+ /// ...
+ /// }
+ ///
+ /// where the ModRMDecision structure is printed as described in the
+ /// documentation for emitModRMDecision(). emitOpcodeDecision() passes on a
+ /// stream and indent level for the UID tables generated by
+ /// emitModRMDecision(), but does not use them itself.
+ ///
+ /// @param o1 - The output stream to print the ID tables generated by
+ /// emitModRMDecision() to.
+ /// @param o2 - The output stream for the decision structure itself.
+ /// @param i1 - The indent level to use with stream o1.
+ /// @param i2 - The indent level to use with stream o2.
+ /// @param decision - The OpcodeDecision to emit along with its subsidiary
+ /// structures.
+ void emitOpcodeDecision(raw_ostream &o1,
+ raw_ostream &o2,
+ uint32_t &i1,
+ uint32_t &i2,
+ OpcodeDecision &decision) const;
+
+ /// emitContextDecision - Emits a ContextDecision and all its subsidiary
+ /// Opcode and ModRMDecisions. A ContextDecision is printed as:
+ ///
+ /// struct ContextDecision NAME = {
+ /// { /* OpcodeDecisions */
+ /// /* IC */
+ /// { /* struct OpcodeDecision */
+ /// ...
+ /// },
+ /// ...
+ /// }
+ /// }
+ ///
+ /// NAME is the name of the ContextDecision (typically one of the four names
+ /// ONEBYTE_SYM, TWOBYTE_SYM, THREEBYTE38_SYM, and THREEBYTE3A_SYM from
+ /// X86DisassemblerDecoderCommon.h).
+ /// IC is one of the contexts in InstructionContext. There is an opcode
+ /// decision for each possible context.
+ /// The OpcodeDecision structures are printed as described in the
+ /// documentation for emitOpcodeDecision.
+ ///
+ /// @param o1 - The output stream to print the ID tables generated by
+ /// emitModRMDecision() to.
+ /// @param o2 - The output stream to print the decision structure to.
+ /// @param i1 - The indent level to use with stream o1.
+ /// @param i2 - The indent level to use with stream o2.
+ /// @param decision - The ContextDecision to emit along with its subsidiary
+ /// structures.
+ /// @param name - The name for the ContextDecision.
+ void emitContextDecision(raw_ostream &o1,
+ raw_ostream &o2,
+ uint32_t &i1,
+ uint32_t &i2,
+ ContextDecision &decision,
+ const char* name) const;
+
+ /// emitInstructionInfo - Prints the instruction specifier table, which has
+ /// one entry for each instruction, and contains name and operand
+ /// information. This table is printed as:
+ ///
+ /// struct InstructionSpecifier CONTEXTS_SYM[k] = {
+ /// {
+ /// /* nnnn */
+ /// "MNEMONIC",
+ /// 0xnn,
+ /// {
+ /// {
+ /// ENCODING,
+ /// TYPE
+ /// },
+ /// ...
+ /// }
+ /// },
+ /// };
+ ///
+ /// k is the total number of instructions.
+ /// nnnn is the ID of the current instruction (0-based). This table
+ /// includes entries for non-instructions like PHINODE.
+ /// 0xnn is the lowest possible opcode for the current instruction, used for
+ /// AddRegFrm instructions to compute the operand's value.
+ /// ENCODING and TYPE describe the encoding and type for a single operand.
+ ///
+ /// @param o - The output stream to which the instruction table should be
+ /// written.
+ /// @param i - The indent level for use with the stream.
+ void emitInstructionInfo(raw_ostream &o, uint32_t &i) const;
+
+ /// emitContextTable - Prints the table that is used to translate from an
+ /// instruction attribute mask to an instruction context. This table is
+ /// printed as:
+ ///
+ /// InstructionContext CONTEXTS_STR[256] = {
+ /// IC, /* 0x00 */
+ /// ...
+ /// };
+ ///
+ /// IC is the context corresponding to the mask 0x00, and there are 256
+ /// possible masks.
+ ///
+ /// @param o - The output stream to which the context table should be written.
+ /// @param i - The indent level for use with the stream.
+ void emitContextTable(raw_ostream &o, uint32_t &i) const;
+
+ /// emitContextDecisions - Prints all four ContextDecision structures using
+ /// emitContextDecision().
+ ///
+ /// @param o1 - The output stream to print the ID tables generated by
+ /// emitModRMDecision() to.
+ /// @param o2 - The output stream to print the decision structures to.
+ /// @param i1 - The indent level to use with stream o1.
+ /// @param i2 - The indent level to use with stream o2.
+ void emitContextDecisions(raw_ostream &o1,
+ raw_ostream &o2,
+ uint32_t &i1,
+ uint32_t &i2) const;
+
+ /// setTableFields - Uses a ModRMFilter to set the appropriate entries in a
+ /// ModRMDecision to refer to a particular instruction ID.
+ ///
+ /// @param decision - The ModRMDecision to populate.
+ /// @param filter - The filter to use in deciding which entries to populate.
+ /// @param uid - The unique ID to set matching entries to.
+ /// @param opcode - The opcode of the instruction, for error reporting.
+ void setTableFields(ModRMDecision &decision,
+ const ModRMFilter &filter,
+ InstrUID uid,
+ uint8_t opcode);
+public:
+ /// Constructor - Allocates space for the class decisions and clears them.
+ DisassemblerTables();
+
+ ~DisassemblerTables();
+
+ /// emit - Emits the instruction table, context table, and class decisions.
+ ///
+ /// @param o - The output stream to print the tables to.
+ void emit(raw_ostream &o) const;
+
+ /// setTableFields - Uses the opcode type, instruction context, opcode, and a
+ /// ModRMFilter as criteria to set a particular set of entries in the
+ /// decode tables to point to a specific uid.
+ ///
+ /// @param type - The opcode type (ONEBYTE, TWOBYTE, etc.)
+ /// @param insnContext - The context to use (IC, IC_64BIT, etc.)
+ /// @param opcode - The last byte of the opcode (not counting any escape
+ /// or extended opcodes).
+ /// @param filter - The ModRMFilter that decides which ModR/M byte values
+ /// correspond to the desired instruction.
+ /// @param uid - The unique ID of the instruction.
+ void setTableFields(OpcodeType type,
+ InstructionContext insnContext,
+ uint8_t opcode,
+ const ModRMFilter &filter,
+ InstrUID uid);
+
+ /// specForUID - Returns the instruction specifier for a given unique
+ /// instruction ID. Used when resolving collisions.
+ ///
+ /// @param uid - The unique ID of the instruction.
+ /// @return - A reference to the instruction specifier.
+ InstructionSpecifier& specForUID(InstrUID uid) {
+ if (uid >= InstructionSpecifiers.size())
+ InstructionSpecifiers.resize(uid + 1);
+
+ return InstructionSpecifiers[uid];
+ }
+
+ // hasConflicts - Reports whether there were primary decode conflicts
+ // from any instructions added to the tables.
+ // @return - true if there were; false otherwise.
+
+ bool hasConflicts() {
+ return HasConflicts;
+ }
+};
+
+} // namespace X86Disassembler
+
+} // namespace llvm
+
+#endif
diff --git a/utils/TableGen/X86ModRMFilters.h b/utils/TableGen/X86ModRMFilters.h
new file mode 100644
index 0000000..45cb07a
--- /dev/null
+++ b/utils/TableGen/X86ModRMFilters.h
@@ -0,0 +1,197 @@
+//===- X86ModRMFilters.h - 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.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is part of the X86 Disassembler Emitter.
+// It contains ModR/M filters that determine which values of the ModR/M byte
+// are valid for a partiuclar instruction.
+// Documentation for the disassembler emitter in general can be found in
+// X86DisasemblerEmitter.h.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef X86MODRMFILTERS_H
+#define X86MODRMFILTERS_H
+
+#include "llvm/System/DataTypes.h"
+
+namespace llvm {
+
+namespace X86Disassembler {
+
+/// ModRMFilter - Abstract base class for clases that recognize patterns in
+/// ModR/M bytes.
+class ModRMFilter {
+public:
+ /// Destructor - Override as necessary.
+ virtual ~ModRMFilter() { }
+
+ /// isDumb - Indicates whether this filter returns the same value for
+ /// any value of the ModR/M byte.
+ ///
+ /// @result - True if the filter returns the same value for any ModR/M
+ /// byte; false if not.
+ virtual bool isDumb() const { return false; }
+
+ /// accepts - Indicates whether the filter accepts a particular ModR/M
+ /// byte value.
+ ///
+ /// @result - True if the filter accepts the ModR/M byte; false if not.
+ virtual bool accepts(uint8_t modRM) const = 0;
+};
+
+/// DumbFilter - Accepts any ModR/M byte. Used for instructions that do not
+/// require a ModR/M byte or instructions where the entire ModR/M byte is used
+/// for operands.
+class DumbFilter : public ModRMFilter {
+public:
+ bool isDumb() const {
+ return true;
+ }
+
+ bool accepts(uint8_t modRM) const {
+ return true;
+ }
+};
+
+/// ModFilter - Filters based on the mod bits [bits 7-6] of the ModR/M byte.
+/// Some instructions are classified based on whether they are 11 or anything
+/// else. This filter performs that classification.
+class ModFilter : public ModRMFilter {
+private:
+ bool R;
+public:
+ /// Constructor
+ ///
+ /// @r - True if the mod bits of the ModR/M byte must be 11; false
+ /// otherwise. The name r derives from the fact that the mod
+ /// bits indicate whether the R/M bits [bits 2-0] signify a
+ /// register or a memory operand.
+ ModFilter(bool r) :
+ ModRMFilter(),
+ R(r) {
+ }
+
+ bool accepts(uint8_t modRM) const {
+ if (R == ((modRM & 0xc0) == 0xc0))
+ return true;
+ else
+ return false;
+ }
+};
+
+/// EscapeFilter - Filters escape opcodes, which are classified in two ways. If
+/// the ModR/M byte is between 0xc0 and 0xff, then there is one slot for each
+/// 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:
+ bool C0_FF;
+ uint8_t NNN_or_ModRM;
+public:
+ /// Constructor
+ ///
+ /// @c0_ff - True if the ModR/M byte must fall between 0xc0 and 0xff;
+ /// false otherwise.
+ /// @nnn_or_modRM - If c0_ff is true, the required value of the entire ModR/M
+ /// byte. If c0_ff is false, the required value of the nnn
+ /// field.
+ EscapeFilter(bool c0_ff, uint8_t nnn_or_modRM) :
+ ModRMFilter(),
+ C0_FF(c0_ff),
+ NNN_or_ModRM(nnn_or_modRM) {
+ }
+
+ bool accepts(uint8_t modRM) const {
+ if ((C0_FF && modRM >= 0xc0 && (modRM == NNN_or_ModRM)) ||
+ (!C0_FF && modRM < 0xc0 && ((modRM & 0x38) >> 3) == NNN_or_ModRM))
+ return true;
+ else
+ return false;
+ }
+};
+
+/// AddRegEscapeFilter - Some escape opcodes have one of the register operands
+/// added to the ModR/M byte, meaning that a range of eight ModR/M values
+/// maps to a single instruction. Such instructions require the ModR/M byte
+/// to fall between 0xc0 and 0xff.
+class AddRegEscapeFilter : public ModRMFilter {
+private:
+ uint8_t ModRM;
+public:
+ /// Constructor
+ ///
+ /// @modRM - The value of the ModR/M byte when the register operand
+ /// refers to the first register in the register set.
+ AddRegEscapeFilter(uint8_t modRM) : ModRM(modRM) {
+ }
+
+ bool accepts(uint8_t modRM) const {
+ if (modRM >= ModRM && modRM < ModRM + 8)
+ return true;
+ else
+ return false;
+ }
+};
+
+/// 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:
+ bool R;
+ uint8_t NNN;
+public:
+ /// Constructor
+ ///
+ /// @r - True if the mod field must be set to 11; false otherwise.
+ /// The name is explained at ModFilter.
+ /// @nnn - The required value of the nnn field.
+ ExtendedFilter(bool r, uint8_t nnn) :
+ ModRMFilter(),
+ R(r),
+ NNN(nnn) {
+ }
+
+ bool accepts(uint8_t modRM) const {
+ if (((R && ((modRM & 0xc0) == 0xc0)) ||
+ (!R && ((modRM & 0xc0) != 0xc0))) &&
+ (((modRM & 0x38) >> 3) == NNN))
+ return true;
+ else
+ return false;
+ }
+};
+
+/// 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:
+ uint8_t ModRM;
+public:
+ /// Constructor
+ ///
+ /// @modRM - The required value of the full ModR/M byte.
+ ExactFilter(uint8_t modRM) :
+ ModRMFilter(),
+ ModRM(modRM) {
+ }
+
+ bool accepts(uint8_t modRM) const {
+ if (ModRM == modRM)
+ return true;
+ else
+ return false;
+ }
+};
+
+} // namespace X86Disassembler
+
+} // namespace llvm
+
+#endif
diff --git a/utils/TableGen/X86RecognizableInstr.cpp b/utils/TableGen/X86RecognizableInstr.cpp
new file mode 100644
index 0000000..2b6e30d
--- /dev/null
+++ b/utils/TableGen/X86RecognizableInstr.cpp
@@ -0,0 +1,959 @@
+//===- X86RecognizableInstr.cpp - Disassembler instruction spec --*- 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 X86 Disassembler Emitter.
+// It contains the implementation of a single recognizable instruction.
+// Documentation for the disassembler emitter in general can be found in
+// X86DisasemblerEmitter.h.
+//
+//===----------------------------------------------------------------------===//
+
+#include "X86DisassemblerShared.h"
+#include "X86RecognizableInstr.h"
+#include "X86ModRMFilters.h"
+
+#include "llvm/Support/ErrorHandling.h"
+
+#include <string>
+
+using namespace llvm;
+
+// A clone of X86 since we can't depend on something that is generated.
+namespace X86Local {
+ enum {
+ Pseudo = 0,
+ RawFrm = 1,
+ AddRegFrm = 2,
+ MRMDestReg = 3,
+ MRMDestMem = 4,
+ MRMSrcReg = 5,
+ MRMSrcMem = 6,
+ MRM0r = 16, MRM1r = 17, MRM2r = 18, MRM3r = 19,
+ MRM4r = 20, MRM5r = 21, MRM6r = 22, MRM7r = 23,
+ MRM0m = 24, MRM1m = 25, MRM2m = 26, MRM3m = 27,
+ MRM4m = 28, MRM5m = 29, MRM6m = 30, MRM7m = 31,
+ MRMInitReg = 32
+ };
+
+ enum {
+ TB = 1,
+ REP = 2,
+ D8 = 3, D9 = 4, DA = 5, DB = 6,
+ DC = 7, DD = 8, DE = 9, DF = 10,
+ XD = 11, XS = 12,
+ T8 = 13, TA = 14
+ };
+}
+
+#define ONE_BYTE_EXTENSION_TABLES \
+ EXTENSION_TABLE(80) \
+ EXTENSION_TABLE(81) \
+ EXTENSION_TABLE(82) \
+ EXTENSION_TABLE(83) \
+ EXTENSION_TABLE(8f) \
+ EXTENSION_TABLE(c0) \
+ EXTENSION_TABLE(c1) \
+ EXTENSION_TABLE(c6) \
+ EXTENSION_TABLE(c7) \
+ EXTENSION_TABLE(d0) \
+ EXTENSION_TABLE(d1) \
+ EXTENSION_TABLE(d2) \
+ EXTENSION_TABLE(d3) \
+ EXTENSION_TABLE(f6) \
+ EXTENSION_TABLE(f7) \
+ EXTENSION_TABLE(fe) \
+ EXTENSION_TABLE(ff)
+
+#define TWO_BYTE_EXTENSION_TABLES \
+ EXTENSION_TABLE(00) \
+ EXTENSION_TABLE(01) \
+ EXTENSION_TABLE(18) \
+ EXTENSION_TABLE(71) \
+ EXTENSION_TABLE(72) \
+ EXTENSION_TABLE(73) \
+ EXTENSION_TABLE(ae) \
+ EXTENSION_TABLE(b9) \
+ EXTENSION_TABLE(ba) \
+ EXTENSION_TABLE(c7)
+
+#define TWO_BYTE_FULL_EXTENSION_TABLES \
+ EXTENSION_TABLE(01)
+
+
+using namespace X86Disassembler;
+
+/// needsModRMForDecode - Indicates whether a particular instruction requires a
+/// ModR/M byte for the instruction to be properly decoded. For example, a
+/// MRMDestReg instruction needs the Mod field in the ModR/M byte to be set to
+/// 0b11.
+///
+/// @param form - The form of the instruction.
+/// @return - true if the form implies that a ModR/M byte is required, false
+/// otherwise.
+static bool needsModRMForDecode(uint8_t form) {
+ if (form == X86Local::MRMDestReg ||
+ form == X86Local::MRMDestMem ||
+ form == X86Local::MRMSrcReg ||
+ form == X86Local::MRMSrcMem ||
+ (form >= X86Local::MRM0r && form <= X86Local::MRM7r) ||
+ (form >= X86Local::MRM0m && form <= X86Local::MRM7m))
+ return true;
+ else
+ return false;
+}
+
+/// isRegFormat - Indicates whether a particular form requires the Mod field of
+/// the ModR/M byte to be 0b11.
+///
+/// @param form - The form of the instruction.
+/// @return - true if the form implies that Mod must be 0b11, false
+/// otherwise.
+static bool isRegFormat(uint8_t form) {
+ if (form == X86Local::MRMDestReg ||
+ form == X86Local::MRMSrcReg ||
+ (form >= X86Local::MRM0r && form <= X86Local::MRM7r))
+ return true;
+ else
+ return false;
+}
+
+/// byteFromBitsInit - Extracts a value at most 8 bits in width from a BitsInit.
+/// Useful for switch statements and the like.
+///
+/// @param init - A reference to the BitsInit to be decoded.
+/// @return - The field, with the first bit in the BitsInit as the lowest
+/// order bit.
+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;
+}
+
+/// byteFromRec - Extract a value at most 8 bits in with from a Record given the
+/// name of the field.
+///
+/// @param rec - The record from which to extract the value.
+/// @param name - The name of the field in the record.
+/// @return - The field, as translated by byteFromBitsInit().
+static uint8_t byteFromRec(const Record* rec, const std::string &name) {
+ BitsInit* bits = rec->getValueAsBitsInit(name);
+ return byteFromBitsInit(*bits);
+}
+
+RecognizableInstr::RecognizableInstr(DisassemblerTables &tables,
+ const CodeGenInstruction &insn,
+ InstrUID uid) {
+ UID = uid;
+
+ Rec = insn.TheDef;
+ Name = Rec->getName();
+ Spec = &tables.specForUID(UID);
+
+ if (!Rec->isSubClassOf("X86Inst")) {
+ ShouldBeEmitted = false;
+ return;
+ }
+
+ Prefix = byteFromRec(Rec, "Prefix");
+ Opcode = byteFromRec(Rec, "Opcode");
+ Form = byteFromRec(Rec, "FormBits");
+ SegOvr = byteFromRec(Rec, "SegOvrBits");
+
+ HasOpSizePrefix = Rec->getValueAsBit("hasOpSizePrefix");
+ HasREX_WPrefix = Rec->getValueAsBit("hasREX_WPrefix");
+ HasLockPrefix = Rec->getValueAsBit("hasLockPrefix");
+ IsCodeGenOnly = Rec->getValueAsBit("isCodeGenOnly");
+
+ Name = Rec->getName();
+ AsmString = Rec->getValueAsString("AsmString");
+
+ Operands = &insn.OperandList;
+
+ IsSSE = HasOpSizePrefix && (Name.find("16") == Name.npos);
+ HasFROperands = false;
+
+ ShouldBeEmitted = true;
+}
+
+void RecognizableInstr::processInstr(DisassemblerTables &tables,
+ const CodeGenInstruction &insn,
+ InstrUID uid)
+{
+ RecognizableInstr recogInstr(tables, insn, uid);
+
+ recogInstr.emitInstructionSpecifier(tables);
+
+ if (recogInstr.shouldBeEmitted())
+ recogInstr.emitDecodePath(tables);
+}
+
+InstructionContext RecognizableInstr::insnContext() const {
+ InstructionContext insnContext;
+
+ if (Name.find("64") != Name.npos || HasREX_WPrefix) {
+ if (HasREX_WPrefix && HasOpSizePrefix)
+ insnContext = IC_64BIT_REXW_OPSIZE;
+ else if (HasOpSizePrefix)
+ insnContext = IC_64BIT_OPSIZE;
+ else if (HasREX_WPrefix && Prefix == X86Local::XS)
+ insnContext = IC_64BIT_REXW_XS;
+ else if (HasREX_WPrefix && Prefix == X86Local::XD)
+ insnContext = IC_64BIT_REXW_XD;
+ else if (Prefix == X86Local::XD)
+ insnContext = IC_64BIT_XD;
+ else if (Prefix == X86Local::XS)
+ insnContext = IC_64BIT_XS;
+ else if (HasREX_WPrefix)
+ insnContext = IC_64BIT_REXW;
+ else
+ insnContext = IC_64BIT;
+ } else {
+ if (HasOpSizePrefix)
+ insnContext = IC_OPSIZE;
+ else if (Prefix == X86Local::XD)
+ insnContext = IC_XD;
+ else if (Prefix == X86Local::XS)
+ insnContext = IC_XS;
+ else
+ insnContext = IC;
+ }
+
+ return insnContext;
+}
+
+RecognizableInstr::filter_ret RecognizableInstr::filter() const {
+ // Filter out intrinsics
+
+ if (!Rec->isSubClassOf("X86Inst"))
+ return FILTER_STRONG;
+
+ if (Form == X86Local::Pseudo ||
+ IsCodeGenOnly)
+ return FILTER_STRONG;
+
+ // Filter out instructions with a LOCK prefix;
+ // prefer forms that do not have the prefix
+ if (HasLockPrefix)
+ return FILTER_WEAK;
+
+ // Filter out artificial instructions
+
+ if (Name.find("TAILJMP") != Name.npos ||
+ Name.find("_Int") != Name.npos ||
+ Name.find("_int") != Name.npos ||
+ Name.find("Int_") != Name.npos ||
+ Name.find("_NOREX") != Name.npos ||
+ Name.find("EH_RETURN") != Name.npos ||
+ Name.find("V_SET") != Name.npos ||
+ Name.find("LOCK_") != Name.npos ||
+ Name.find("WIN") != Name.npos)
+ return FILTER_STRONG;
+
+ // Special cases.
+
+ if (Name.find("PCMPISTRI") != Name.npos && Name != "PCMPISTRI")
+ return FILTER_WEAK;
+ if (Name.find("PCMPESTRI") != Name.npos && Name != "PCMPESTRI")
+ return FILTER_WEAK;
+
+ if (Name.find("MOV") != Name.npos && Name.find("r0") != Name.npos)
+ return FILTER_WEAK;
+ if (Name.find("MOVZ") != Name.npos && Name.find("MOVZX") == Name.npos)
+ 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" ||
+ Name == "MOVPQI2QImr" ||
+ Name == "MOVSDmr" ||
+ Name == "MOVSDrm" ||
+ Name == "MOVSSmr" ||
+ Name == "MOVSSrm" ||
+ Name == "MMX_MOVD64rrv164" ||
+ Name == "CRC32m16" ||
+ Name == "MOV64ri64i32" ||
+ Name == "CRC32r16")
+ return FILTER_WEAK;
+
+ // Filter out instructions with segment override prefixes.
+ // They're too messy to handle now and we'll special case them if needed.
+
+ if (SegOvr)
+ return FILTER_STRONG;
+
+ // Filter out instructions that can't be printed.
+
+ if (AsmString.size() == 0)
+ return FILTER_STRONG;
+
+ // Filter out instructions with subreg operands.
+
+ if (AsmString.find("subreg") != AsmString.npos)
+ return FILTER_STRONG;
+
+ assert(Form != X86Local::MRMInitReg &&
+ "FORMAT_MRMINITREG instruction not skipped");
+
+ if (HasFROperands && Name.find("MOV") != Name.npos &&
+ ((Name.find("2") != Name.npos && Name.find("32") == Name.npos) ||
+ (Name.find("to") != Name.npos)))
+ return FILTER_WEAK;
+
+ return FILTER_NORMAL;
+}
+
+void RecognizableInstr::handleOperand(
+ bool optional,
+ unsigned &operandIndex,
+ unsigned &physicalOperandIndex,
+ unsigned &numPhysicalOperands,
+ unsigned *operandMapping,
+ OperandEncoding (*encodingFromString)(const std::string&, bool hasOpSizePrefix)) {
+ if (optional) {
+ if (physicalOperandIndex >= numPhysicalOperands)
+ return;
+ } else {
+ assert(physicalOperandIndex < numPhysicalOperands);
+ }
+
+ while (operandMapping[operandIndex] != operandIndex) {
+ Spec->operands[operandIndex].encoding = ENCODING_DUP;
+ Spec->operands[operandIndex].type =
+ (OperandType)(TYPE_DUP0 + operandMapping[operandIndex]);
+ ++operandIndex;
+ }
+
+ const std::string &typeName = (*Operands)[operandIndex].Rec->getName();
+
+ Spec->operands[operandIndex].encoding = encodingFromString(typeName,
+ HasOpSizePrefix);
+ Spec->operands[operandIndex].type = typeFromString(typeName,
+ IsSSE,
+ HasREX_WPrefix,
+ HasOpSizePrefix);
+
+ ++operandIndex;
+ ++physicalOperandIndex;
+}
+
+void RecognizableInstr::emitInstructionSpecifier(DisassemblerTables &tables) {
+ Spec->name = Name;
+
+ if (!Rec->isSubClassOf("X86Inst"))
+ return;
+
+ switch (filter()) {
+ case FILTER_WEAK:
+ Spec->filtered = true;
+ break;
+ case FILTER_STRONG:
+ ShouldBeEmitted = false;
+ return;
+ case FILTER_NORMAL:
+ break;
+ }
+
+ Spec->insnContext = insnContext();
+
+ const std::vector<CodeGenInstruction::OperandInfo> &OperandList = *Operands;
+
+ unsigned operandIndex;
+ unsigned numOperands = OperandList.size();
+ unsigned numPhysicalOperands = 0;
+
+ // operandMapping maps from operands in OperandList to their originals.
+ // If operandMapping[i] != i, then the entry is a duplicate.
+ unsigned operandMapping[X86_MAX_OPERANDS];
+
+ bool hasFROperands = false;
+
+ assert(numOperands < X86_MAX_OPERANDS && "X86_MAX_OPERANDS is not large enough");
+
+ for (operandIndex = 0; operandIndex < numOperands; ++operandIndex) {
+ if (OperandList[operandIndex].Constraints.size()) {
+ const std::string &constraint = OperandList[operandIndex].Constraints[0];
+ std::string::size_type tiedToPos;
+
+ if ((tiedToPos = constraint.find(" << 16) | (1 << TOI::TIED_TO))")) !=
+ constraint.npos) {
+ tiedToPos--;
+ operandMapping[operandIndex] = constraint[tiedToPos] - '0';
+ } else {
+ ++numPhysicalOperands;
+ operandMapping[operandIndex] = operandIndex;
+ }
+ } else {
+ ++numPhysicalOperands;
+ operandMapping[operandIndex] = operandIndex;
+ }
+
+ const std::string &recName = OperandList[operandIndex].Rec->getName();
+
+ if (recName.find("FR") != recName.npos)
+ hasFROperands = true;
+ }
+
+ if (hasFROperands && Name.find("MOV") != Name.npos &&
+ ((Name.find("2") != Name.npos && Name.find("32") == Name.npos) ||
+ (Name.find("to") != Name.npos)))
+ ShouldBeEmitted = false;
+
+ if (!ShouldBeEmitted)
+ return;
+
+#define HANDLE_OPERAND(class) \
+ handleOperand(false, \
+ operandIndex, \
+ physicalOperandIndex, \
+ numPhysicalOperands, \
+ operandMapping, \
+ class##EncodingFromString);
+
+#define HANDLE_OPTIONAL(class) \
+ handleOperand(true, \
+ operandIndex, \
+ physicalOperandIndex, \
+ numPhysicalOperands, \
+ operandMapping, \
+ class##EncodingFromString);
+
+ // operandIndex should always be < numOperands
+ operandIndex = 0;
+ // physicalOperandIndex should always be < numPhysicalOperands
+ unsigned physicalOperandIndex = 0;
+
+ switch (Form) {
+ case X86Local::RawFrm:
+ // Operand 1 (optional) is an address or immediate.
+ // Operand 2 (optional) is an immediate.
+ assert(numPhysicalOperands <= 2 &&
+ "Unexpected number of operands for RawFrm");
+ HANDLE_OPTIONAL(relocation)
+ HANDLE_OPTIONAL(immediate)
+ break;
+ case X86Local::AddRegFrm:
+ // Operand 1 is added to the opcode.
+ // Operand 2 (optional) is an address.
+ assert(numPhysicalOperands >= 1 && numPhysicalOperands <= 2 &&
+ "Unexpected number of operands for AddRegFrm");
+ HANDLE_OPERAND(opcodeModifier)
+ HANDLE_OPTIONAL(relocation)
+ break;
+ case X86Local::MRMDestReg:
+ // Operand 1 is a register operand in the R/M field.
+ // Operand 2 is a register operand in the Reg/Opcode field.
+ // Operand 3 (optional) is an immediate.
+ assert(numPhysicalOperands >= 2 && numPhysicalOperands <= 3 &&
+ "Unexpected number of operands for MRMDestRegFrm");
+ HANDLE_OPERAND(rmRegister)
+ HANDLE_OPERAND(roRegister)
+ HANDLE_OPTIONAL(immediate)
+ break;
+ case X86Local::MRMDestMem:
+ // Operand 1 is a memory operand (possibly SIB-extended)
+ // Operand 2 is a register operand in the Reg/Opcode field.
+ // Operand 3 (optional) is an immediate.
+ assert(numPhysicalOperands >= 2 && numPhysicalOperands <= 3 &&
+ "Unexpected number of operands for MRMDestMemFrm");
+ HANDLE_OPERAND(memory)
+ HANDLE_OPERAND(roRegister)
+ HANDLE_OPTIONAL(immediate)
+ break;
+ case X86Local::MRMSrcReg:
+ // Operand 1 is a register operand in the Reg/Opcode field.
+ // Operand 2 is a register operand in the R/M field.
+ // Operand 3 (optional) is an immediate.
+ assert(numPhysicalOperands >= 2 && numPhysicalOperands <= 3 &&
+ "Unexpected number of operands for MRMSrcRegFrm");
+ HANDLE_OPERAND(roRegister)
+ HANDLE_OPERAND(rmRegister)
+ HANDLE_OPTIONAL(immediate)
+ break;
+ case X86Local::MRMSrcMem:
+ // Operand 1 is a register operand in the Reg/Opcode field.
+ // Operand 2 is a memory operand (possibly SIB-extended)
+ // Operand 3 (optional) is an immediate.
+ assert(numPhysicalOperands >= 2 && numPhysicalOperands <= 3 &&
+ "Unexpected number of operands for MRMSrcMemFrm");
+ HANDLE_OPERAND(roRegister)
+ HANDLE_OPERAND(memory)
+ HANDLE_OPTIONAL(immediate)
+ break;
+ case X86Local::MRM0r:
+ case X86Local::MRM1r:
+ case X86Local::MRM2r:
+ case X86Local::MRM3r:
+ case X86Local::MRM4r:
+ case X86Local::MRM5r:
+ case X86Local::MRM6r:
+ case X86Local::MRM7r:
+ // Operand 1 is a register operand in the R/M field.
+ // Operand 2 (optional) is an immediate or relocation.
+ assert(numPhysicalOperands <= 2 &&
+ "Unexpected number of operands for MRMnRFrm");
+ HANDLE_OPTIONAL(rmRegister)
+ HANDLE_OPTIONAL(relocation)
+ 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:
+ // 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");
+ HANDLE_OPERAND(memory)
+ HANDLE_OPTIONAL(relocation)
+ break;
+ case X86Local::MRMInitReg:
+ // Ignored.
+ break;
+ }
+
+ #undef HANDLE_OPERAND
+ #undef HANDLE_OPTIONAL
+}
+
+void RecognizableInstr::emitDecodePath(DisassemblerTables &tables) const {
+ // Special cases where the LLVM tables are not complete
+
+#define EXACTCASE(class, name, lastbyte) \
+ if (Name == name) { \
+ tables.setTableFields(class, \
+ insnContext(), \
+ Opcode, \
+ ExactFilter(lastbyte), \
+ UID); \
+ Spec->modifierBase = Opcode; \
+ return; \
+ }
+
+ EXACTCASE(TWOBYTE, "MONITOR", 0xc8)
+ EXACTCASE(TWOBYTE, "MWAIT", 0xc9)
+ EXACTCASE(TWOBYTE, "SWPGS", 0xf8)
+ EXACTCASE(TWOBYTE, "INVEPT", 0x80)
+ EXACTCASE(TWOBYTE, "INVVPID", 0x81)
+ EXACTCASE(TWOBYTE, "VMCALL", 0xc1)
+ EXACTCASE(TWOBYTE, "VMLAUNCH", 0xc2)
+ EXACTCASE(TWOBYTE, "VMRESUME", 0xc3)
+ EXACTCASE(TWOBYTE, "VMXOFF", 0xc4)
+
+ if (Name == "INVLPG") {
+ tables.setTableFields(TWOBYTE,
+ insnContext(),
+ Opcode,
+ ExtendedFilter(false, 7),
+ UID);
+ Spec->modifierBase = Opcode;
+ return;
+ }
+
+ OpcodeType opcodeType = (OpcodeType)-1;
+
+ ModRMFilter* filter = NULL;
+ uint8_t opcodeToSet = 0;
+
+ switch (Prefix) {
+ // Extended two-byte opcodes can start with f2 0f, f3 0f, or 0f
+ case X86Local::XD:
+ case X86Local::XS:
+ case X86Local::TB:
+ opcodeType = TWOBYTE;
+
+ switch (Opcode) {
+#define EXTENSION_TABLE(n) case 0x##n:
+ TWO_BYTE_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;
+ } // switch (Form)
+ break;
+ default:
+ if (needsModRMForDecode(Form))
+ filter = new ModFilter(isRegFormat(Form));
+ else
+ filter = new DumbFilter();
+
+ break;
+ } // switch (opcode)
+ opcodeToSet = Opcode;
+ break;
+ case X86Local::T8:
+ opcodeType = THREEBYTE_38;
+ if (needsModRMForDecode(Form))
+ filter = new ModFilter(isRegFormat(Form));
+ else
+ filter = new DumbFilter();
+ opcodeToSet = Opcode;
+ break;
+ case X86Local::TA:
+ opcodeType = THREEBYTE_3A;
+ if (needsModRMForDecode(Form))
+ filter = new ModFilter(isRegFormat(Form));
+ else
+ filter = new DumbFilter();
+ opcodeToSet = Opcode;
+ break;
+ case X86Local::D8:
+ case X86Local::D9:
+ case X86Local::DA:
+ case X86Local::DB:
+ case X86Local::DC:
+ case X86Local::DD:
+ case X86Local::DE:
+ case X86Local::DF:
+ assert(Opcode >= 0xc0 && "Unexpected opcode for an escape opcode");
+ opcodeType = ONEBYTE;
+ if (Form == X86Local::AddRegFrm) {
+ Spec->modifierType = MODIFIER_MODRM;
+ Spec->modifierBase = Opcode;
+ filter = new AddRegEscapeFilter(Opcode);
+ } else {
+ filter = new EscapeFilter(true, Opcode);
+ }
+ opcodeToSet = 0xd8 + (Prefix - X86Local::D8);
+ break;
+ default:
+ opcodeType = ONEBYTE;
+ switch (Opcode) {
+#define EXTENSION_TABLE(n) case 0x##n:
+ ONE_BYTE_EXTENSION_TABLES
+#undef EXTENSION_TABLE
+ switch (Form) {
+ default:
+ llvm_unreachable("Fell through the cracks of a single-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;
+ } // switch (Form)
+ break;
+ case 0xd8:
+ case 0xd9:
+ case 0xda:
+ case 0xdb:
+ case 0xdc:
+ case 0xdd:
+ case 0xde:
+ case 0xdf:
+ filter = new EscapeFilter(false, Form - X86Local::MRM0m);
+ break;
+ default:
+ if (needsModRMForDecode(Form))
+ filter = new ModFilter(isRegFormat(Form));
+ else
+ filter = new DumbFilter();
+ break;
+ } // switch (Opcode)
+ opcodeToSet = Opcode;
+ } // switch (Prefix)
+
+ assert(opcodeType != (OpcodeType)-1 &&
+ "Opcode type not set");
+ assert(filter && "Filter not set");
+
+ if (Form == X86Local::AddRegFrm) {
+ if(Spec->modifierType != MODIFIER_MODRM) {
+ assert(opcodeToSet < 0xf9 &&
+ "Not enough room for all ADDREG_FRM operands");
+
+ uint8_t currentOpcode;
+
+ for (currentOpcode = opcodeToSet;
+ currentOpcode < opcodeToSet + 8;
+ ++currentOpcode)
+ tables.setTableFields(opcodeType,
+ insnContext(),
+ currentOpcode,
+ *filter,
+ UID);
+
+ Spec->modifierType = MODIFIER_OPCODE;
+ Spec->modifierBase = opcodeToSet;
+ } else {
+ // modifierBase was set where MODIFIER_MODRM was set
+ tables.setTableFields(opcodeType,
+ insnContext(),
+ opcodeToSet,
+ *filter,
+ UID);
+ }
+ } else {
+ tables.setTableFields(opcodeType,
+ insnContext(),
+ opcodeToSet,
+ *filter,
+ UID);
+
+ Spec->modifierType = MODIFIER_NONE;
+ Spec->modifierBase = opcodeToSet;
+ }
+
+ delete filter;
+}
+
+#define TYPE(str, type) if (s == str) return type;
+OperandType RecognizableInstr::typeFromString(const std::string &s,
+ bool isSSE,
+ bool hasREX_WPrefix,
+ bool hasOpSizePrefix) {
+ if (isSSE) {
+ // For SSE instructions, we ignore the OpSize prefix and force operand
+ // sizes.
+ TYPE("GR16", TYPE_R16)
+ TYPE("GR32", TYPE_R32)
+ TYPE("GR64", TYPE_R64)
+ }
+ if(hasREX_WPrefix) {
+ // For instructions with a REX_W prefix, a declared 32-bit register encoding
+ // is special.
+ TYPE("GR32", TYPE_R32)
+ }
+ if(!hasOpSizePrefix) {
+ // For instructions without an OpSize prefix, a declared 16-bit register or
+ // immediate encoding is special.
+ TYPE("GR16", TYPE_R16)
+ TYPE("i16imm", TYPE_IMM16)
+ }
+ TYPE("i16mem", TYPE_Mv)
+ TYPE("i16imm", TYPE_IMMv)
+ TYPE("i16i8imm", TYPE_IMMv)
+ TYPE("GR16", TYPE_Rv)
+ TYPE("i32mem", TYPE_Mv)
+ TYPE("i32imm", TYPE_IMMv)
+ TYPE("i32i8imm", TYPE_IMM32)
+ TYPE("GR32", TYPE_Rv)
+ TYPE("i64mem", TYPE_Mv)
+ TYPE("i64i32imm", TYPE_IMM64)
+ TYPE("i64i8imm", TYPE_IMM64)
+ TYPE("GR64", TYPE_R64)
+ TYPE("i8mem", TYPE_M8)
+ TYPE("i8imm", TYPE_IMM8)
+ TYPE("GR8", TYPE_R8)
+ TYPE("VR128", TYPE_XMM128)
+ TYPE("f128mem", TYPE_M128)
+ TYPE("FR64", TYPE_XMM64)
+ TYPE("f64mem", TYPE_M64FP)
+ TYPE("FR32", TYPE_XMM32)
+ TYPE("f32mem", TYPE_M32FP)
+ TYPE("RST", TYPE_ST)
+ TYPE("i128mem", TYPE_M128)
+ TYPE("i64i32imm_pcrel", TYPE_REL64)
+ TYPE("i32imm_pcrel", TYPE_REL32)
+ TYPE("SSECC", TYPE_IMM8)
+ TYPE("brtarget", TYPE_RELv)
+ TYPE("brtarget8", TYPE_REL8)
+ TYPE("f80mem", TYPE_M80FP)
+ TYPE("lea32mem", TYPE_LEA)
+ TYPE("lea64_32mem", TYPE_LEA)
+ TYPE("lea64mem", TYPE_LEA)
+ TYPE("VR64", TYPE_MM64)
+ TYPE("i64imm", TYPE_IMMv)
+ TYPE("opaque32mem", TYPE_M1616)
+ TYPE("opaque48mem", TYPE_M1632)
+ TYPE("opaque80mem", TYPE_M1664)
+ TYPE("opaque512mem", TYPE_M512)
+ TYPE("SEGMENT_REG", TYPE_SEGMENTREG)
+ TYPE("DEBUG_REG", TYPE_DEBUGREG)
+ TYPE("CONTROL_REG_32", TYPE_CR32)
+ TYPE("CONTROL_REG_64", TYPE_CR64)
+ TYPE("offset8", TYPE_MOFFS8)
+ TYPE("offset16", TYPE_MOFFS16)
+ TYPE("offset32", TYPE_MOFFS32)
+ TYPE("offset64", TYPE_MOFFS64)
+ errs() << "Unhandled type string " << s << "\n";
+ llvm_unreachable("Unhandled type string");
+}
+#undef TYPE
+
+#define ENCODING(str, encoding) if (s == str) return encoding;
+OperandEncoding RecognizableInstr::immediateEncodingFromString
+ (const std::string &s,
+ bool hasOpSizePrefix) {
+ if(!hasOpSizePrefix) {
+ // For instructions without an OpSize prefix, a declared 16-bit register or
+ // immediate encoding is special.
+ ENCODING("i16imm", ENCODING_IW)
+ }
+ ENCODING("i32i8imm", ENCODING_IB)
+ ENCODING("SSECC", ENCODING_IB)
+ ENCODING("i16imm", ENCODING_Iv)
+ ENCODING("i16i8imm", ENCODING_IB)
+ ENCODING("i32imm", ENCODING_Iv)
+ ENCODING("i64i32imm", ENCODING_ID)
+ ENCODING("i64i8imm", ENCODING_IB)
+ ENCODING("i8imm", ENCODING_IB)
+ errs() << "Unhandled immediate encoding " << s << "\n";
+ llvm_unreachable("Unhandled immediate encoding");
+}
+
+OperandEncoding RecognizableInstr::rmRegisterEncodingFromString
+ (const std::string &s,
+ bool hasOpSizePrefix) {
+ ENCODING("GR16", ENCODING_RM)
+ ENCODING("GR32", ENCODING_RM)
+ ENCODING("GR64", ENCODING_RM)
+ ENCODING("GR8", ENCODING_RM)
+ ENCODING("VR128", ENCODING_RM)
+ ENCODING("FR64", ENCODING_RM)
+ ENCODING("FR32", ENCODING_RM)
+ ENCODING("VR64", ENCODING_RM)
+ errs() << "Unhandled R/M register encoding " << s << "\n";
+ llvm_unreachable("Unhandled R/M register encoding");
+}
+
+OperandEncoding RecognizableInstr::roRegisterEncodingFromString
+ (const std::string &s,
+ bool hasOpSizePrefix) {
+ ENCODING("GR16", ENCODING_REG)
+ ENCODING("GR32", ENCODING_REG)
+ ENCODING("GR64", ENCODING_REG)
+ ENCODING("GR8", ENCODING_REG)
+ ENCODING("VR128", ENCODING_REG)
+ ENCODING("FR64", ENCODING_REG)
+ ENCODING("FR32", ENCODING_REG)
+ ENCODING("VR64", ENCODING_REG)
+ ENCODING("SEGMENT_REG", ENCODING_REG)
+ ENCODING("DEBUG_REG", ENCODING_REG)
+ ENCODING("CONTROL_REG_32", ENCODING_REG)
+ ENCODING("CONTROL_REG_64", ENCODING_REG)
+ errs() << "Unhandled reg/opcode register encoding " << s << "\n";
+ llvm_unreachable("Unhandled reg/opcode register encoding");
+}
+
+OperandEncoding RecognizableInstr::memoryEncodingFromString
+ (const std::string &s,
+ bool hasOpSizePrefix) {
+ ENCODING("i16mem", ENCODING_RM)
+ ENCODING("i32mem", ENCODING_RM)
+ ENCODING("i64mem", ENCODING_RM)
+ ENCODING("i8mem", ENCODING_RM)
+ ENCODING("f128mem", ENCODING_RM)
+ ENCODING("f64mem", ENCODING_RM)
+ ENCODING("f32mem", ENCODING_RM)
+ ENCODING("i128mem", ENCODING_RM)
+ ENCODING("f80mem", ENCODING_RM)
+ ENCODING("lea32mem", ENCODING_RM)
+ ENCODING("lea64_32mem", ENCODING_RM)
+ ENCODING("lea64mem", ENCODING_RM)
+ ENCODING("opaque32mem", ENCODING_RM)
+ ENCODING("opaque48mem", ENCODING_RM)
+ ENCODING("opaque80mem", ENCODING_RM)
+ ENCODING("opaque512mem", ENCODING_RM)
+ errs() << "Unhandled memory encoding " << s << "\n";
+ llvm_unreachable("Unhandled memory encoding");
+}
+
+OperandEncoding RecognizableInstr::relocationEncodingFromString
+ (const std::string &s,
+ bool hasOpSizePrefix) {
+ if(!hasOpSizePrefix) {
+ // For instructions without an OpSize prefix, a declared 16-bit register or
+ // immediate encoding is special.
+ ENCODING("i16imm", ENCODING_IW)
+ }
+ ENCODING("i16imm", ENCODING_Iv)
+ ENCODING("i16i8imm", ENCODING_IB)
+ ENCODING("i32imm", ENCODING_Iv)
+ ENCODING("i32i8imm", ENCODING_IB)
+ ENCODING("i64i32imm", ENCODING_ID)
+ ENCODING("i64i8imm", ENCODING_IB)
+ ENCODING("i8imm", ENCODING_IB)
+ ENCODING("i64i32imm_pcrel", ENCODING_ID)
+ ENCODING("i32imm_pcrel", ENCODING_ID)
+ ENCODING("brtarget", ENCODING_Iv)
+ ENCODING("brtarget8", ENCODING_IB)
+ ENCODING("i64imm", ENCODING_IO)
+ ENCODING("offset8", ENCODING_Ia)
+ ENCODING("offset16", ENCODING_Ia)
+ ENCODING("offset32", ENCODING_Ia)
+ ENCODING("offset64", ENCODING_Ia)
+ errs() << "Unhandled relocation encoding " << s << "\n";
+ llvm_unreachable("Unhandled relocation encoding");
+}
+
+OperandEncoding RecognizableInstr::opcodeModifierEncodingFromString
+ (const std::string &s,
+ bool hasOpSizePrefix) {
+ ENCODING("RST", ENCODING_I)
+ ENCODING("GR32", ENCODING_Rv)
+ ENCODING("GR64", ENCODING_RO)
+ ENCODING("GR16", ENCODING_Rv)
+ ENCODING("GR8", ENCODING_RB)
+ errs() << "Unhandled opcode modifier encoding " << s << "\n";
+ llvm_unreachable("Unhandled opcode modifier encoding");
+}
+#undef ENCODING
diff --git a/utils/TableGen/X86RecognizableInstr.h b/utils/TableGen/X86RecognizableInstr.h
new file mode 100644
index 0000000..84374b0
--- /dev/null
+++ b/utils/TableGen/X86RecognizableInstr.h
@@ -0,0 +1,237 @@
+//===- X86RecognizableInstr.h - Disassembler instruction spec ----*- 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 X86 Disassembler Emitter.
+// It contains the interface of a single recognizable instruction.
+// Documentation for the disassembler emitter in general can be found in
+// X86DisasemblerEmitter.h.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef X86RECOGNIZABLEINSTR_H
+#define X86RECOGNIZABLEINSTR_H
+
+#include "X86DisassemblerTables.h"
+
+#include "CodeGenTarget.h"
+#include "Record.h"
+
+#include "llvm/System/DataTypes.h"
+#include "llvm/ADT/SmallVector.h"
+
+namespace llvm {
+
+namespace X86Disassembler {
+
+/// RecognizableInstr - Encapsulates all information required to decode a single
+/// instruction, as extracted from the LLVM instruction tables. Has methods
+/// to interpret the information available in the LLVM tables, and to emit the
+/// instruction into DisassemblerTables.
+class RecognizableInstr {
+private:
+ /// The opcode of the instruction, as used in an MCInst
+ InstrUID UID;
+ /// The record from the .td files corresponding to this instruction
+ const Record* Rec;
+ /// The prefix field from the record
+ uint8_t Prefix;
+ /// The opcode field from the record; this is the opcode used in the Intel
+ /// encoding and therefore distinct from the UID
+ uint8_t Opcode;
+ /// The form field from the record
+ uint8_t Form;
+ /// The segment override field from the record
+ uint8_t SegOvr;
+ /// The hasOpSizePrefix field from the record
+ bool HasOpSizePrefix;
+ /// The hasREX_WPrefix field from the record
+ bool HasREX_WPrefix;
+ /// The hasLockPrefix field from the record
+ bool HasLockPrefix;
+ /// The isCodeGenOnly filed from the record
+ bool IsCodeGenOnly;
+
+ /// The instruction name as listed in the tables
+ std::string Name;
+ /// The AT&T AsmString for the instruction
+ std::string AsmString;
+
+ /// Indicates whether the instruction is SSE
+ bool IsSSE;
+ /// Indicates whether the instruction has FR operands - MOVs with FR operands
+ /// are typically ignored
+ bool HasFROperands;
+ /// Indicates whether the instruction should be emitted into the decode
+ /// tables; regardless, it will be emitted into the instruction info table
+ bool ShouldBeEmitted;
+
+ /// The operands of the instruction, as listed in the CodeGenInstruction.
+ /// They are not one-to-one with operands listed in the MCInst; for example,
+ /// memory operands expand to 5 operands in the MCInst
+ const std::vector<CodeGenInstruction::OperandInfo>* Operands;
+ /// The description of the instruction that is emitted into the instruction
+ /// info table
+ InstructionSpecifier* Spec;
+
+ /// insnContext - Returns the primary context in which the instruction is
+ /// valid.
+ ///
+ /// @return - The context in which the instruction is valid.
+ InstructionContext insnContext() const;
+
+ enum filter_ret {
+ FILTER_STRONG, // instruction has no place in the instruction tables
+ FILTER_WEAK, // instruction may conflict, and should be eliminated if
+ // it does
+ FILTER_NORMAL // instruction should have high priority and generate an
+ // error if it conflcits with any other FILTER_NORMAL
+ // instruction
+ };
+
+ /// filter - Determines whether the instruction should be decodable. Some
+ /// instructions are pure intrinsics and use unencodable operands; many
+ /// synthetic instructions are duplicates of other instructions; other
+ /// instructions only differ in the logical way in which they are used, and
+ /// have the same decoding. Because these would cause decode conflicts,
+ /// they must be filtered out.
+ ///
+ /// @return - The degree of filtering to be applied (see filter_ret).
+ filter_ret filter() const;
+
+ /// typeFromString - Translates an operand type from the string provided in
+ /// the LLVM tables to an OperandType for use in the operand specifier.
+ ///
+ /// @param s - The string, as extracted by calling Rec->getName()
+ /// on a CodeGenInstruction::OperandInfo.
+ /// @param isSSE - Indicates whether the instruction is an SSE
+ /// instruction. For SSE instructions, immediates are
+ /// fixed-size rather than being affected by the
+ /// mandatory OpSize prefix.
+ /// @param hasREX_WPrefix - Indicates whether the instruction has a REX.W
+ /// prefix. If it does, 32-bit register operands stay
+ /// 32-bit regardless of the operand size.
+ /// @param hasOpSizePrefix- Indicates whether the instruction has an OpSize
+ /// prefix. If it does not, then 16-bit register
+ /// operands stay 16-bit.
+ /// @return - The operand's type.
+ static OperandType typeFromString(const std::string& s,
+ bool isSSE,
+ bool hasREX_WPrefix,
+ bool hasOpSizePrefix);
+
+ /// immediateEncodingFromString - Translates an immediate encoding from the
+ /// string provided in the LLVM tables to an OperandEncoding for use in
+ /// the operand specifier.
+ ///
+ /// @param s - See typeFromString().
+ /// @param hasOpSizePrefix - Indicates whether the instruction has an OpSize
+ /// prefix. If it does not, then 16-bit immediate
+ /// operands stay 16-bit.
+ /// @return - The operand's encoding.
+ static OperandEncoding immediateEncodingFromString(const std::string &s,
+ bool hasOpSizePrefix);
+
+ /// rmRegisterEncodingFromString - Like immediateEncodingFromString, but
+ /// handles operands that are in the REG field of the ModR/M byte.
+ static OperandEncoding rmRegisterEncodingFromString(const std::string &s,
+ bool hasOpSizePrefix);
+
+ /// rmRegisterEncodingFromString - Like immediateEncodingFromString, but
+ /// handles operands that are in the REG field of the ModR/M byte.
+ static OperandEncoding roRegisterEncodingFromString(const std::string &s,
+ bool hasOpSizePrefix);
+ static OperandEncoding memoryEncodingFromString(const std::string &s,
+ bool hasOpSizePrefix);
+ static OperandEncoding relocationEncodingFromString(const std::string &s,
+ bool hasOpSizePrefix);
+ static OperandEncoding opcodeModifierEncodingFromString(const std::string &s,
+ bool hasOpSizePrefix);
+
+ /// handleOperand - Converts a single operand from the LLVM table format to
+ /// the emitted table format, handling any duplicate operands it encounters
+ /// and then one non-duplicate.
+ ///
+ /// @param optional - Determines whether to assert that the
+ /// operand exists.
+ /// @param operandIndex - The index into the generated operand table.
+ /// Incremented by this function one or more
+ /// times to reflect possible duplicate
+ /// operands).
+ /// @param physicalOperandIndex - The index of the current operand into the
+ /// set of non-duplicate ('physical') operands.
+ /// Incremented by this function once.
+ /// @param numPhysicalOperands - The number of non-duplicate operands in the
+ /// instructions.
+ /// @param operandMapping - The operand mapping, which has an entry for
+ /// each operand that indicates whether it is a
+ /// duplicate, and of what.
+ void handleOperand(bool optional,
+ unsigned &operandIndex,
+ unsigned &physicalOperandIndex,
+ unsigned &numPhysicalOperands,
+ unsigned *operandMapping,
+ OperandEncoding (*encodingFromString)
+ (const std::string&,
+ bool hasOpSizePrefix));
+
+ /// shouldBeEmitted - Returns the shouldBeEmitted field. Although filter()
+ /// filters out many instructions, at various points in decoding we
+ /// determine that the instruction should not actually be decodable. In
+ /// particular, MMX MOV instructions aren't emitted, but they're only
+ /// identified during operand parsing.
+ ///
+ /// @return - true if at this point we believe the instruction should be
+ /// emitted; false if not. This will return false if filter() returns false
+ /// once emitInstructionSpecifier() has been called.
+ bool shouldBeEmitted() const {
+ return ShouldBeEmitted;
+ }
+
+ /// emitInstructionSpecifier - Loads the instruction specifier for the current
+ /// instruction into a DisassemblerTables.
+ ///
+ /// @arg tables - The DisassemblerTables to populate with the specifier for
+ /// the current instruction.
+ void emitInstructionSpecifier(DisassemblerTables &tables);
+
+ /// emitDecodePath - Populates the proper fields in the decode tables
+ /// corresponding to the decode paths for this instruction.
+ ///
+ /// @arg tables - The DisassemblerTables to populate with the decode
+ /// decode information for the current instruction.
+ void emitDecodePath(DisassemblerTables &tables) const;
+
+ /// Constructor - Initializes a RecognizableInstr with the appropriate fields
+ /// from a CodeGenInstruction.
+ ///
+ /// @arg tables - The DisassemblerTables that the specifier will be added to.
+ /// @arg insn - The CodeGenInstruction to extract information from.
+ /// @arg uid - The unique ID of the current instruction.
+ RecognizableInstr(DisassemblerTables &tables,
+ const CodeGenInstruction &insn,
+ InstrUID uid);
+public:
+ /// processInstr - Accepts a CodeGenInstruction and loads decode information
+ /// for it into a DisassemblerTables if appropriate.
+ ///
+ /// @arg tables - The DiassemblerTables to be populated with decode
+ /// information.
+ /// @arg insn - The CodeGenInstruction to be used as a source for this
+ /// information.
+ /// @uid - The unique ID of the instruction.
+ static void processInstr(DisassemblerTables &tables,
+ const CodeGenInstruction &insn,
+ InstrUID uid);
+};
+
+} // namespace X86Disassembler
+
+} // namespace llvm
+
+#endif
diff --git a/utils/buildit/build_llvm b/utils/buildit/build_llvm
index 4392b27..25f6554 100755
--- a/utils/buildit/build_llvm
+++ b/utils/buildit/build_llvm
@@ -243,7 +243,11 @@ if ! test $? == 0 ; then
fi
# Install Version.h
-RC_ProjectSourceSubversion=`printf "%d" $LLVM_SUBMIT_SUBVERSION`
+LLVM_MINOR_VERSION=`echo $LLVM_SUBMIT_SUBVERSION | sed -e 's,0*\([1-9][0-9]*\),\1,'`
+if [ "x$LLVM_MINOR_VERSION" = "x" ]; then
+ LLVM_MINOR_VERSION=0
+fi
+RC_ProjectSourceSubversion=`printf "%d" $LLVM_MINOR_VERSION`
echo "#define LLVM_VERSION ${RC_ProjectSourceVersion}" > $DEST_DIR$DEST_ROOT/include/llvm/Version.h
echo "#define LLVM_MINOR_VERSION ${RC_ProjectSourceSubversion}" >> $DEST_DIR$DEST_ROOT/include/llvm/Version.h
diff --git a/utils/emacs/llvm-mode.el b/utils/emacs/llvm-mode.el
index 55c56da..b1af853 100644
--- a/utils/emacs/llvm-mode.el
+++ b/utils/emacs/llvm-mode.el
@@ -122,7 +122,7 @@
;; Associate .ll files with llvm-mode
(setq auto-mode-alist
- (append '(("\\.ll$" . llvm-mode) ("\\.llx$" . llvm-mode)) auto-mode-alist))
+ (append '(("\\.ll$" . llvm-mode)) auto-mode-alist))
(provide 'llvm-mode)
;; end of llvm-mode.el
diff --git a/utils/lit/TestFormats.py b/utils/lit/TestFormats.py
index 7305c79..5dfd54a 100644
--- a/utils/lit/TestFormats.py
+++ b/utils/lit/TestFormats.py
@@ -9,12 +9,17 @@ class GoogleTest(object):
self.test_sub_dir = str(test_sub_dir)
self.test_suffix = str(test_suffix)
- def getGTestTests(self, path):
+ def getGTestTests(self, path, litConfig):
"""getGTestTests(path) - [name]
Return the tests available in gtest executable."""
- lines = Util.capture([path, '--gtest_list_tests']).split('\n')
+ try:
+ lines = Util.capture([path, '--gtest_list_tests']).split('\n')
+ except:
+ litConfig.error("unable to discover google-tests in %r" % path)
+ raise StopIteration
+
nested_tests = []
for ln in lines:
if not ln.strip():
@@ -47,7 +52,7 @@ class GoogleTest(object):
execpath = os.path.join(filepath, subfilename)
# Discover the tests in this executable.
- for name in self.getGTestTests(execpath):
+ for name in self.getGTestTests(execpath, litConfig):
testPath = path_in_suite + (filename, subfilename, name)
yield Test.Test(testSuite, testPath, localConfig)
diff --git a/utils/lit/lit.py b/utils/lit/lit.py
index 293976f..851063b 100755
--- a/utils/lit/lit.py
+++ b/utils/lit/lit.py
@@ -1,576 +1,5 @@
#!/usr/bin/env python
-"""
-lit - LLVM Integrated Tester.
-
-See lit.pod for more information.
-"""
-
-import math, os, platform, random, re, sys, time, threading, traceback
-
-import ProgressBar
-import TestRunner
-import Util
-
-from TestingConfig import TestingConfig
-import LitConfig
-import Test
-
-# Configuration files to look for when discovering test suites. These can be
-# overridden with --config-prefix.
-#
-# FIXME: Rename to 'config.lit', 'site.lit', and 'local.lit' ?
-gConfigName = 'lit.cfg'
-gSiteConfigName = 'lit.site.cfg'
-
-kLocalConfigName = 'lit.local.cfg'
-
-class TestingProgressDisplay:
- def __init__(self, opts, numTests, progressBar=None):
- self.opts = opts
- self.numTests = numTests
- self.current = None
- self.lock = threading.Lock()
- self.progressBar = progressBar
- self.completed = 0
-
- def update(self, test):
- # Avoid locking overhead in quiet mode
- if self.opts.quiet and not test.result.isFailure:
- self.completed += 1
- return
-
- # Output lock.
- self.lock.acquire()
- try:
- self.handleUpdate(test)
- finally:
- self.lock.release()
-
- def finish(self):
- if self.progressBar:
- self.progressBar.clear()
- elif self.opts.quiet:
- pass
- elif self.opts.succinct:
- sys.stdout.write('\n')
-
- def handleUpdate(self, test):
- self.completed += 1
- if self.progressBar:
- self.progressBar.update(float(self.completed)/self.numTests,
- test.getFullName())
-
- if self.opts.succinct and not test.result.isFailure:
- return
-
- if self.progressBar:
- self.progressBar.clear()
-
- print '%s: %s (%d of %d)' % (test.result.name, test.getFullName(),
- self.completed, self.numTests)
-
- if test.result.isFailure and self.opts.showOutput:
- print "%s TEST '%s' FAILED %s" % ('*'*20, test.getFullName(),
- '*'*20)
- print test.output
- print "*" * 20
-
- sys.stdout.flush()
-
-class TestProvider:
- def __init__(self, tests, maxTime):
- self.maxTime = maxTime
- self.iter = iter(tests)
- self.lock = threading.Lock()
- self.startTime = time.time()
-
- def get(self):
- # Check if we have run out of time.
- if self.maxTime is not None:
- if time.time() - self.startTime > self.maxTime:
- return None
-
- # Otherwise take the next test.
- self.lock.acquire()
- try:
- item = self.iter.next()
- except StopIteration:
- item = None
- self.lock.release()
- return item
-
-class Tester(threading.Thread):
- def __init__(self, litConfig, provider, display):
- threading.Thread.__init__(self)
- self.litConfig = litConfig
- self.provider = provider
- self.display = display
-
- def run(self):
- while 1:
- item = self.provider.get()
- if item is None:
- break
- self.runTest(item)
-
- def runTest(self, test):
- result = None
- startTime = time.time()
- try:
- result, output = test.config.test_format.execute(test,
- self.litConfig)
- except KeyboardInterrupt:
- # This is a sad hack. Unfortunately subprocess goes
- # bonkers with ctrl-c and we start forking merrily.
- print '\nCtrl-C detected, goodbye.'
- os.kill(0,9)
- except:
- if self.litConfig.debug:
- raise
- result = Test.UNRESOLVED
- output = 'Exception during script execution:\n'
- output += traceback.format_exc()
- output += '\n'
- elapsed = time.time() - startTime
-
- test.setResult(result, output, elapsed)
- self.display.update(test)
-
-def dirContainsTestSuite(path):
- cfgpath = os.path.join(path, gSiteConfigName)
- if os.path.exists(cfgpath):
- return cfgpath
- cfgpath = os.path.join(path, gConfigName)
- if os.path.exists(cfgpath):
- return cfgpath
-
-def getTestSuite(item, litConfig, cache):
- """getTestSuite(item, litConfig, cache) -> (suite, relative_path)
-
- Find the test suite containing @arg item.
-
- @retval (None, ...) - Indicates no test suite contains @arg item.
- @retval (suite, relative_path) - The suite that @arg item is in, and its
- relative path inside that suite.
- """
- def search1(path):
- # Check for a site config or a lit config.
- cfgpath = dirContainsTestSuite(path)
-
- # If we didn't find a config file, keep looking.
- if not cfgpath:
- parent,base = os.path.split(path)
- if parent == path:
- return (None, ())
-
- ts, relative = search(parent)
- return (ts, relative + (base,))
-
- # We found a config file, load it.
- if litConfig.debug:
- litConfig.note('loading suite config %r' % cfgpath)
-
- cfg = TestingConfig.frompath(cfgpath, None, litConfig, mustExist = True)
- source_root = os.path.realpath(cfg.test_source_root or path)
- exec_root = os.path.realpath(cfg.test_exec_root or path)
- return Test.TestSuite(cfg.name, source_root, exec_root, cfg), ()
-
- def search(path):
- # Check for an already instantiated test suite.
- res = cache.get(path)
- if res is None:
- cache[path] = res = search1(path)
- return res
-
- # Canonicalize the path.
- item = os.path.realpath(item)
-
- # Skip files and virtual components.
- components = []
- while not os.path.isdir(item):
- parent,base = os.path.split(item)
- if parent == item:
- return (None, ())
- components.append(base)
- item = parent
- components.reverse()
-
- ts, relative = search(item)
- return ts, tuple(relative + tuple(components))
-
-def getLocalConfig(ts, path_in_suite, litConfig, cache):
- def search1(path_in_suite):
- # Get the parent config.
- if not path_in_suite:
- parent = ts.config
- else:
- parent = search(path_in_suite[:-1])
-
- # Load the local configuration.
- source_path = ts.getSourcePath(path_in_suite)
- cfgpath = os.path.join(source_path, kLocalConfigName)
- if litConfig.debug:
- litConfig.note('loading local config %r' % cfgpath)
- return TestingConfig.frompath(cfgpath, parent, litConfig,
- mustExist = False,
- config = parent.clone(cfgpath))
-
- def search(path_in_suite):
- key = (ts, path_in_suite)
- res = cache.get(key)
- if res is None:
- cache[key] = res = search1(path_in_suite)
- return res
-
- return search(path_in_suite)
-
-def getTests(path, litConfig, testSuiteCache, localConfigCache):
- # Find the test suite for this input and its relative path.
- ts,path_in_suite = getTestSuite(path, litConfig, testSuiteCache)
- if ts is None:
- litConfig.warning('unable to find test suite for %r' % path)
- return (),()
-
- if litConfig.debug:
- litConfig.note('resolved input %r to %r::%r' % (path, ts.name,
- path_in_suite))
-
- return ts, getTestsInSuite(ts, path_in_suite, litConfig,
- testSuiteCache, localConfigCache)
-
-def getTestsInSuite(ts, path_in_suite, litConfig,
- testSuiteCache, localConfigCache):
- # Check that the source path exists (errors here are reported by the
- # caller).
- source_path = ts.getSourcePath(path_in_suite)
- if not os.path.exists(source_path):
- return
-
- # Check if the user named a test directly.
- if not os.path.isdir(source_path):
- lc = getLocalConfig(ts, path_in_suite[:-1], litConfig, localConfigCache)
- yield Test.Test(ts, path_in_suite, lc)
- return
-
- # Otherwise we have a directory to search for tests, start by getting the
- # local configuration.
- lc = getLocalConfig(ts, path_in_suite, litConfig, localConfigCache)
-
- # Search for tests.
- for res in lc.test_format.getTestsInDirectory(ts, path_in_suite,
- litConfig, lc):
- yield res
-
- # Search subdirectories.
- for filename in os.listdir(source_path):
- # FIXME: This doesn't belong here?
- if filename in ('Output', '.svn') or filename in lc.excludes:
- continue
-
- # Ignore non-directories.
- file_sourcepath = os.path.join(source_path, filename)
- if not os.path.isdir(file_sourcepath):
- continue
-
- # Check for nested test suites, first in the execpath in case there is a
- # site configuration and then in the source path.
- file_execpath = ts.getExecPath(path_in_suite + (filename,))
- if dirContainsTestSuite(file_execpath):
- sub_ts, subiter = getTests(file_execpath, litConfig,
- testSuiteCache, localConfigCache)
- elif dirContainsTestSuite(file_sourcepath):
- sub_ts, subiter = getTests(file_sourcepath, litConfig,
- testSuiteCache, localConfigCache)
- else:
- # Otherwise, continue loading from inside this test suite.
- subiter = getTestsInSuite(ts, path_in_suite + (filename,),
- litConfig, testSuiteCache,
- localConfigCache)
- sub_ts = None
-
- N = 0
- for res in subiter:
- N += 1
- yield res
- if sub_ts and not N:
- litConfig.warning('test suite %r contained no tests' % sub_ts.name)
-
-def runTests(numThreads, litConfig, provider, display):
- # If only using one testing thread, don't use threads at all; this lets us
- # profile, among other things.
- if numThreads == 1:
- t = Tester(litConfig, provider, display)
- t.run()
- return
-
- # Otherwise spin up the testing threads and wait for them to finish.
- testers = [Tester(litConfig, provider, display)
- for i in range(numThreads)]
- for t in testers:
- t.start()
- try:
- for t in testers:
- t.join()
- except KeyboardInterrupt:
- sys.exit(2)
-
-def main():
- global options
- from optparse import OptionParser, OptionGroup
- parser = OptionParser("usage: %prog [options] {file-or-path}")
-
- parser.add_option("-j", "--threads", dest="numThreads", metavar="N",
- help="Number of testing threads",
- type=int, action="store", default=None)
- parser.add_option("", "--config-prefix", dest="configPrefix",
- metavar="NAME", help="Prefix for 'lit' config files",
- action="store", default=None)
- parser.add_option("", "--param", dest="userParameters",
- metavar="NAME=VAL",
- help="Add 'NAME' = 'VAL' to the user defined parameters",
- type=str, action="append", default=[])
-
- group = OptionGroup(parser, "Output Format")
- # FIXME: I find these names very confusing, although I like the
- # functionality.
- group.add_option("-q", "--quiet", dest="quiet",
- help="Suppress no error output",
- action="store_true", default=False)
- group.add_option("-s", "--succinct", dest="succinct",
- help="Reduce amount of output",
- action="store_true", default=False)
- group.add_option("-v", "--verbose", dest="showOutput",
- help="Show all test output",
- action="store_true", default=False)
- group.add_option("", "--no-progress-bar", dest="useProgressBar",
- help="Do not use curses based progress bar",
- action="store_false", default=True)
- parser.add_option_group(group)
-
- group = OptionGroup(parser, "Test Execution")
- group.add_option("", "--path", dest="path",
- help="Additional paths to add to testing environment",
- action="append", type=str, default=[])
- group.add_option("", "--vg", dest="useValgrind",
- help="Run tests under valgrind",
- action="store_true", default=False)
- group.add_option("", "--vg-arg", dest="valgrindArgs", metavar="ARG",
- help="Specify an extra argument for valgrind",
- type=str, action="append", default=[])
- group.add_option("", "--time-tests", dest="timeTests",
- help="Track elapsed wall time for each test",
- action="store_true", default=False)
- group.add_option("", "--no-execute", dest="noExecute",
- help="Don't execute any tests (assume PASS)",
- action="store_true", default=False)
- parser.add_option_group(group)
-
- group = OptionGroup(parser, "Test Selection")
- group.add_option("", "--max-tests", dest="maxTests", metavar="N",
- help="Maximum number of tests to run",
- action="store", type=int, default=None)
- group.add_option("", "--max-time", dest="maxTime", metavar="N",
- help="Maximum time to spend testing (in seconds)",
- action="store", type=float, default=None)
- group.add_option("", "--shuffle", dest="shuffle",
- help="Run tests in random order",
- action="store_true", default=False)
- parser.add_option_group(group)
-
- group = OptionGroup(parser, "Debug and Experimental Options")
- group.add_option("", "--debug", dest="debug",
- help="Enable debugging (for 'lit' development)",
- action="store_true", default=False)
- group.add_option("", "--show-suites", dest="showSuites",
- help="Show discovered test suites",
- action="store_true", default=False)
- group.add_option("", "--no-tcl-as-sh", dest="useTclAsSh",
- help="Don't run Tcl scripts using 'sh'",
- action="store_false", default=True)
- group.add_option("", "--repeat", dest="repeatTests", metavar="N",
- help="Repeat tests N times (for timing)",
- action="store", default=None, type=int)
- parser.add_option_group(group)
-
- (opts, args) = parser.parse_args()
-
- if not args:
- parser.error('No inputs specified')
-
- if opts.configPrefix is not None:
- global gConfigName, gSiteConfigName
- gConfigName = '%s.cfg' % opts.configPrefix
- gSiteConfigName = '%s.site.cfg' % opts.configPrefix
-
- if opts.numThreads is None:
- opts.numThreads = Util.detectCPUs()
-
- inputs = args
-
- # Create the user defined parameters.
- userParams = {}
- for entry in opts.userParameters:
- if '=' not in entry:
- name,val = entry,''
- else:
- name,val = entry.split('=', 1)
- userParams[name] = val
-
- # Create the global config object.
- litConfig = LitConfig.LitConfig(progname = os.path.basename(sys.argv[0]),
- path = opts.path,
- quiet = opts.quiet,
- useValgrind = opts.useValgrind,
- valgrindArgs = opts.valgrindArgs,
- useTclAsSh = opts.useTclAsSh,
- noExecute = opts.noExecute,
- debug = opts.debug,
- isWindows = (platform.system()=='Windows'),
- params = userParams)
-
- # Load the tests from the inputs.
- tests = []
- testSuiteCache = {}
- localConfigCache = {}
- for input in inputs:
- prev = len(tests)
- tests.extend(getTests(input, litConfig,
- testSuiteCache, localConfigCache)[1])
- if prev == len(tests):
- litConfig.warning('input %r contained no tests' % input)
-
- # If there were any errors during test discovery, exit now.
- if litConfig.numErrors:
- print >>sys.stderr, '%d errors, exiting.' % litConfig.numErrors
- sys.exit(2)
-
- if opts.showSuites:
- suitesAndTests = dict([(ts,[])
- for ts,_ in testSuiteCache.values()
- if ts])
- for t in tests:
- suitesAndTests[t.suite].append(t)
-
- print '-- Test Suites --'
- suitesAndTests = suitesAndTests.items()
- suitesAndTests.sort(key = lambda (ts,_): ts.name)
- for ts,ts_tests in suitesAndTests:
- print ' %s - %d tests' %(ts.name, len(ts_tests))
- print ' Source Root: %s' % ts.source_root
- print ' Exec Root : %s' % ts.exec_root
-
- # Select and order the tests.
- numTotalTests = len(tests)
- if opts.shuffle:
- random.shuffle(tests)
- else:
- tests.sort(key = lambda t: t.getFullName())
- if opts.maxTests is not None:
- tests = tests[:opts.maxTests]
-
- extra = ''
- if len(tests) != numTotalTests:
- extra = ' of %d' % numTotalTests
- header = '-- Testing: %d%s tests, %d threads --'%(len(tests),extra,
- opts.numThreads)
-
- if opts.repeatTests:
- tests = [t.copyWithIndex(i)
- for t in tests
- for i in range(opts.repeatTests)]
-
- progressBar = None
- if not opts.quiet:
- if opts.succinct and opts.useProgressBar:
- try:
- tc = ProgressBar.TerminalController()
- progressBar = ProgressBar.ProgressBar(tc, header)
- except ValueError:
- print header
- progressBar = ProgressBar.SimpleProgressBar('Testing: ')
- else:
- print header
-
- # Don't create more threads than tests.
- opts.numThreads = min(len(tests), opts.numThreads)
-
- startTime = time.time()
- display = TestingProgressDisplay(opts, len(tests), progressBar)
- provider = TestProvider(tests, opts.maxTime)
- runTests(opts.numThreads, litConfig, provider, display)
- display.finish()
-
- if not opts.quiet:
- print 'Testing Time: %.2fs'%(time.time() - startTime)
-
- # Update results for any tests which weren't run.
- for t in tests:
- if t.result is None:
- t.setResult(Test.UNRESOLVED, '', 0.0)
-
- # List test results organized by kind.
- hasFailures = False
- byCode = {}
- for t in tests:
- if t.result not in byCode:
- byCode[t.result] = []
- byCode[t.result].append(t)
- if t.result.isFailure:
- hasFailures = True
-
- # FIXME: Show unresolved and (optionally) unsupported tests.
- for title,code in (('Unexpected Passing Tests', Test.XPASS),
- ('Failing Tests', Test.FAIL)):
- elts = byCode.get(code)
- if not elts:
- continue
- print '*'*20
- print '%s (%d):' % (title, len(elts))
- for t in elts:
- print ' %s' % t.getFullName()
- print
-
- if opts.timeTests:
- # Collate, in case we repeated tests.
- times = {}
- for t in tests:
- key = t.getFullName()
- times[key] = times.get(key, 0.) + t.elapsed
-
- byTime = list(times.items())
- byTime.sort(key = lambda (name,elapsed): elapsed)
- if byTime:
- Util.printHistogram(byTime, title='Tests')
-
- for name,code in (('Expected Passes ', Test.PASS),
- ('Expected Failures ', Test.XFAIL),
- ('Unsupported Tests ', Test.UNSUPPORTED),
- ('Unresolved Tests ', Test.UNRESOLVED),
- ('Unexpected Passes ', Test.XPASS),
- ('Unexpected Failures', Test.FAIL),):
- if opts.quiet and not code.isFailure:
- continue
- N = len(byCode.get(code,[]))
- if N:
- print ' %s: %d' % (name,N)
-
- # If we encountered any additional errors, exit abnormally.
- if litConfig.numErrors:
- print >>sys.stderr, '\n%d error(s), exiting.' % litConfig.numErrors
- sys.exit(2)
-
- # Warn about warnings.
- if litConfig.numWarnings:
- print >>sys.stderr, '\n%d warning(s) in tests.' % litConfig.numWarnings
-
- if hasFailures:
- sys.exit(1)
- sys.exit(0)
-
if __name__=='__main__':
- # Bump the GIL check interval, its more important to get any one thread to a
- # blocking operation (hopefully exec) than to try and unblock other threads.
- import sys
- sys.setcheckinterval(1000)
- main()
+ import lit
+ lit.main()
diff --git a/utils/lit/lit/ExampleTests.ObjDir/lit.site.cfg b/utils/lit/lit/ExampleTests.ObjDir/lit.site.cfg
new file mode 100644
index 0000000..14b6e01
--- /dev/null
+++ b/utils/lit/lit/ExampleTests.ObjDir/lit.site.cfg
@@ -0,0 +1,15 @@
+# -*- Python -*-
+
+# Site specific configuration file.
+#
+# Typically this will be generated by the build system to automatically set
+# certain configuration variables which cannot be autodetected, so that 'lit'
+# can easily be used on the command line.
+
+import os
+
+# Preserve the obj_root, for use by the main lit.cfg.
+config.example_obj_root = os.path.dirname(__file__)
+
+lit.load_config(config, os.path.join(config.test_source_root,
+ 'lit.cfg'))
diff --git a/utils/lit/lit/ExampleTests/Clang/fsyntax-only.c b/utils/lit/lit/ExampleTests/Clang/fsyntax-only.c
new file mode 100644
index 0000000..a4a064b
--- /dev/null
+++ b/utils/lit/lit/ExampleTests/Clang/fsyntax-only.c
@@ -0,0 +1,4 @@
+// RUN: clang -fsyntax-only -Xclang -verify %s
+
+int f0(void) {} // expected-warning {{control reaches end of non-void function}}
+
diff --git a/utils/lit/lit/ExampleTests/Clang/lit.cfg b/utils/lit/lit/ExampleTests/Clang/lit.cfg
new file mode 100644
index 0000000..114ac60
--- /dev/null
+++ b/utils/lit/lit/ExampleTests/Clang/lit.cfg
@@ -0,0 +1,80 @@
+# -*- Python -*-
+
+# Configuration file for the 'lit' test runner.
+
+# name: The name of this test suite.
+config.name = 'Clang'
+
+# testFormat: The test format to use to interpret tests.
+#
+# For now we require '&&' between commands, until they get globally killed and
+# the test runner updated.
+config.test_format = lit.formats.ShTest(execute_external = True)
+
+# suffixes: A list of file extensions to treat as test files.
+config.suffixes = ['.c', '.cpp', '.m', '.mm']
+
+# target_triple: Used by ShTest and TclTest formats for XFAIL checks.
+config.target_triple = 'foo'
+
+###
+
+# Discover the 'clang' and 'clangcc' to use.
+
+import os
+
+def inferClang(PATH):
+ # Determine which clang to use.
+ clang = os.getenv('CLANG')
+
+ # If the user set clang in the environment, definitely use that and don't
+ # try to validate.
+ if clang:
+ return clang
+
+ # Otherwise look in the path.
+ clang = lit.util.which('clang', PATH)
+
+ if not clang:
+ lit.fatal("couldn't find 'clang' program, try setting "
+ "CLANG in your environment")
+
+ return clang
+
+def inferClangCC(clang, PATH):
+ clangcc = os.getenv('CLANGCC')
+
+ # If the user set clang in the environment, definitely use that and don't
+ # try to validate.
+ if clangcc:
+ return clangcc
+
+ # Otherwise try adding -cc since we expect to be looking in a build
+ # directory.
+ if clang.endswith('.exe'):
+ clangccName = clang[:-4] + '-cc.exe'
+ else:
+ clangccName = clang + '-cc'
+ clangcc = lit.util.which(clangccName, PATH)
+ if not clangcc:
+ # Otherwise ask clang.
+ res = lit.util.capture([clang, '-print-prog-name=clang-cc'])
+ res = res.strip()
+ if res and os.path.exists(res):
+ clangcc = res
+
+ if not clangcc:
+ lit.fatal("couldn't find 'clang-cc' program, try setting "
+ "CLANGCC in your environment")
+
+ return clangcc
+
+clang = inferClang(config.environment['PATH'])
+if not lit.quiet:
+ lit.note('using clang: %r' % clang)
+config.substitutions.append( (' clang ', ' ' + clang + ' ') )
+
+clang_cc = inferClangCC(clang, config.environment['PATH'])
+if not lit.quiet:
+ lit.note('using clang-cc: %r' % clang_cc)
+config.substitutions.append( (' clang-cc ', ' ' + clang_cc + ' ') )
diff --git a/utils/lit/lit/ExampleTests/LLVM.InTree/test/Bar/bar-test.ll b/utils/lit/lit/ExampleTests/LLVM.InTree/test/Bar/bar-test.ll
new file mode 100644
index 0000000..3017b13
--- /dev/null
+++ b/utils/lit/lit/ExampleTests/LLVM.InTree/test/Bar/bar-test.ll
@@ -0,0 +1,3 @@
+; RUN: true
+; XFAIL: *
+; XTARGET: darwin
diff --git a/utils/lit/lit/ExampleTests/LLVM.InTree/test/Bar/dg.exp b/utils/lit/lit/ExampleTests/LLVM.InTree/test/Bar/dg.exp
new file mode 100644
index 0000000..2bda07a
--- /dev/null
+++ b/utils/lit/lit/ExampleTests/LLVM.InTree/test/Bar/dg.exp
@@ -0,0 +1,6 @@
+load_lib llvm.exp
+
+if { [llvm_supports_target X86] } {
+ RunLLVMTests [lsort [glob -nocomplain $srcdir/$subdir/*.{ll}]]
+}
+
diff --git a/utils/lit/lit/ExampleTests/LLVM.InTree/test/lit.cfg b/utils/lit/lit/ExampleTests/LLVM.InTree/test/lit.cfg
new file mode 100644
index 0000000..e7ef037
--- /dev/null
+++ b/utils/lit/lit/ExampleTests/LLVM.InTree/test/lit.cfg
@@ -0,0 +1,151 @@
+# -*- Python -*-
+
+# Configuration file for the 'lit' test runner.
+
+import os
+
+# name: The name of this test suite.
+config.name = 'LLVM'
+
+# testFormat: The test format to use to interpret tests.
+config.test_format = lit.formats.TclTest()
+
+# suffixes: A list of file extensions to treat as test files, this is actually
+# set by on_clone().
+config.suffixes = []
+
+# test_source_root: The root path where tests are located.
+config.test_source_root = os.path.dirname(__file__)
+
+# test_exec_root: The root path where tests should be run.
+llvm_obj_root = getattr(config, 'llvm_obj_root', None)
+if llvm_obj_root is not None:
+ config.test_exec_root = os.path.join(llvm_obj_root, 'test')
+
+###
+
+import os
+
+# Check that the object root is known.
+if config.test_exec_root is None:
+ # Otherwise, we haven't loaded the site specific configuration (the user is
+ # probably trying to run on a test file directly, and either the site
+ # configuration hasn't been created by the build system, or we are in an
+ # out-of-tree build situation).
+
+ # Try to detect the situation where we are using an out-of-tree build by
+ # looking for 'llvm-config'.
+ #
+ # FIXME: I debated (i.e., wrote and threw away) adding logic to
+ # automagically generate the lit.site.cfg if we are in some kind of fresh
+ # build situation. This means knowing how to invoke the build system
+ # though, and I decided it was too much magic.
+
+ llvm_config = lit.util.which('llvm-config', config.environment['PATH'])
+ if not llvm_config:
+ lit.fatal('No site specific configuration available!')
+
+ # Get the source and object roots.
+ llvm_src_root = lit.util.capture(['llvm-config', '--src-root']).strip()
+ llvm_obj_root = lit.util.capture(['llvm-config', '--obj-root']).strip()
+
+ # Validate that we got a tree which points to here.
+ this_src_root = os.path.dirname(config.test_source_root)
+ if os.path.realpath(llvm_src_root) != os.path.realpath(this_src_root):
+ lit.fatal('No site specific configuration available!')
+
+ # Check that the site specific configuration exists.
+ site_cfg = os.path.join(llvm_obj_root, 'test', 'lit.site.cfg')
+ if not os.path.exists(site_cfg):
+ lit.fatal('No site specific configuration available!')
+
+ # Okay, that worked. Notify the user of the automagic, and reconfigure.
+ lit.note('using out-of-tree build at %r' % llvm_obj_root)
+ lit.load_config(config, site_cfg)
+ raise SystemExit
+
+###
+
+# Load site data from DejaGNU's site.exp.
+import re
+site_exp = {}
+# FIXME: Implement lit.site.cfg.
+for line in open(os.path.join(config.llvm_obj_root, 'test', 'site.exp')):
+ m = re.match('set ([^ ]+) "([^"]*)"', line)
+ 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.
+config.target_triple = site_exp['target_triplet']
+
+# Provide llvm_supports_target for use in local configs.
+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
+
+RunLLVMTests \[lsort \[glob -nocomplain \$srcdir/\$subdir/\*\.(.*)\]\]""",
+ re.MULTILINE)
+conditionalLibData = re.compile(r"""load_lib llvm.exp
+
+if.*\[ ?(llvm[^ ]*) ([^ ]*) ?\].*{
+ *RunLLVMTests \[lsort \[glob -nocomplain \$srcdir/\$subdir/\*\.(.*)\]\]
+\}""", re.MULTILINE)
+def on_clone(parent, cfg, for_path):
+ def addSuffixes(match):
+ if match[0] == '{' and match[-1] == '}':
+ cfg.suffixes = ['.' + s for s in match[1:-1].split(',')]
+ else:
+ cfg.suffixes = ['.' + match]
+
+ libPath = os.path.join(os.path.dirname(for_path),
+ 'dg.exp')
+ if not os.path.exists(libPath):
+ cfg.unsupported = True
+ return
+
+ # Reset unsupported, in case we inherited it.
+ cfg.unsupported = False
+ lib = open(libPath).read().strip()
+
+ # Check for a simple library.
+ m = simpleLibData.match(lib)
+ if m:
+ addSuffixes(m.group(1))
+ return
+
+ # Check for a conditional test set.
+ m = conditionalLibData.match(lib)
+ if m:
+ funcname,arg,match = m.groups()
+ addSuffixes(match)
+
+ func = globals().get(funcname)
+ if not func:
+ lit.error('unsupported predicate %r' % funcname)
+ elif not func(arg):
+ cfg.unsupported = True
+ return
+ # Otherwise, give up.
+ lit.error('unable to understand %r:\n%s' % (libPath, lib))
+
+config.on_clone = on_clone
diff --git a/utils/lit/lit/ExampleTests/LLVM.InTree/test/lit.site.cfg b/utils/lit/lit/ExampleTests/LLVM.InTree/test/lit.site.cfg
new file mode 100644
index 0000000..3bfee54
--- /dev/null
+++ b/utils/lit/lit/ExampleTests/LLVM.InTree/test/lit.site.cfg
@@ -0,0 +1,10 @@
+# -*- Python -*-
+
+## Autogenerated by Makefile ##
+# Do not edit!
+
+# Preserve some key paths for use by main LLVM test suite config.
+config.llvm_obj_root = os.path.dirname(os.path.dirname(__file__))
+
+# Let the main config do the real work.
+lit.load_config(config, os.path.join(config.llvm_obj_root, 'test/lit.cfg'))
diff --git a/utils/lit/lit/ExampleTests/LLVM.InTree/test/site.exp b/utils/lit/lit/ExampleTests/LLVM.InTree/test/site.exp
new file mode 100644
index 0000000..1d9c743
--- /dev/null
+++ b/utils/lit/lit/ExampleTests/LLVM.InTree/test/site.exp
@@ -0,0 +1,30 @@
+## these variables are automatically generated by make ##
+# 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 llvmgcc_version "4.2.1"
+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 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 llvmgccmajvers "4"
+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/lit.local.cfg b/utils/lit/lit/ExampleTests/LLVM.OutOfTree/lit.local.cfg
new file mode 100644
index 0000000..80d0c7e
--- /dev/null
+++ b/utils/lit/lit/ExampleTests/LLVM.OutOfTree/lit.local.cfg
@@ -0,0 +1 @@
+config.excludes = ['src']
diff --git a/utils/lit/lit/ExampleTests/LLVM.OutOfTree/obj/test/Foo/lit.local.cfg b/utils/lit/lit/ExampleTests/LLVM.OutOfTree/obj/test/Foo/lit.local.cfg
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/utils/lit/lit/ExampleTests/LLVM.OutOfTree/obj/test/Foo/lit.local.cfg
diff --git a/utils/lit/lit/ExampleTests/LLVM.OutOfTree/obj/test/lit.site.cfg b/utils/lit/lit/ExampleTests/LLVM.OutOfTree/obj/test/lit.site.cfg
new file mode 100644
index 0000000..bdcc35e
--- /dev/null
+++ b/utils/lit/lit/ExampleTests/LLVM.OutOfTree/obj/test/lit.site.cfg
@@ -0,0 +1,11 @@
+# -*- Python -*-
+
+## Autogenerated by Makefile ##
+# Do not edit!
+
+# Preserve some key paths for use by main LLVM test suite config.
+config.llvm_obj_root = os.path.dirname(os.path.dirname(__file__))
+
+# Let the main config do the real work.
+lit.load_config(config, os.path.join(config.llvm_obj_root,
+ '../src/test/lit.cfg'))
diff --git a/utils/lit/lit/ExampleTests/LLVM.OutOfTree/obj/test/site.exp b/utils/lit/lit/ExampleTests/LLVM.OutOfTree/obj/test/site.exp
new file mode 100644
index 0000000..1d9c743
--- /dev/null
+++ b/utils/lit/lit/ExampleTests/LLVM.OutOfTree/obj/test/site.exp
@@ -0,0 +1,30 @@
+## these variables are automatically generated by make ##
+# 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 llvmgcc_version "4.2.1"
+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 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 llvmgccmajvers "4"
+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/Foo/data.txt b/utils/lit/lit/ExampleTests/LLVM.OutOfTree/src/test/Foo/data.txt
new file mode 100644
index 0000000..45b983b
--- /dev/null
+++ b/utils/lit/lit/ExampleTests/LLVM.OutOfTree/src/test/Foo/data.txt
@@ -0,0 +1 @@
+hi
diff --git a/utils/lit/lit/ExampleTests/LLVM.OutOfTree/src/test/Foo/dg.exp b/utils/lit/lit/ExampleTests/LLVM.OutOfTree/src/test/Foo/dg.exp
new file mode 100644
index 0000000..2bda07a
--- /dev/null
+++ b/utils/lit/lit/ExampleTests/LLVM.OutOfTree/src/test/Foo/dg.exp
@@ -0,0 +1,6 @@
+load_lib llvm.exp
+
+if { [llvm_supports_target X86] } {
+ RunLLVMTests [lsort [glob -nocomplain $srcdir/$subdir/*.{ll}]]
+}
+
diff --git a/utils/lit/lit/ExampleTests/LLVM.OutOfTree/src/test/Foo/pct-S.ll b/utils/lit/lit/ExampleTests/LLVM.OutOfTree/src/test/Foo/pct-S.ll
new file mode 100644
index 0000000..4e8a582
--- /dev/null
+++ b/utils/lit/lit/ExampleTests/LLVM.OutOfTree/src/test/Foo/pct-S.ll
@@ -0,0 +1 @@
+; RUN: grep "hi" %S/data.txt \ No newline at end of file
diff --git a/utils/lit/lit/ExampleTests/LLVM.OutOfTree/src/test/lit.cfg b/utils/lit/lit/ExampleTests/LLVM.OutOfTree/src/test/lit.cfg
new file mode 100644
index 0000000..e7ef037
--- /dev/null
+++ b/utils/lit/lit/ExampleTests/LLVM.OutOfTree/src/test/lit.cfg
@@ -0,0 +1,151 @@
+# -*- Python -*-
+
+# Configuration file for the 'lit' test runner.
+
+import os
+
+# name: The name of this test suite.
+config.name = 'LLVM'
+
+# testFormat: The test format to use to interpret tests.
+config.test_format = lit.formats.TclTest()
+
+# suffixes: A list of file extensions to treat as test files, this is actually
+# set by on_clone().
+config.suffixes = []
+
+# test_source_root: The root path where tests are located.
+config.test_source_root = os.path.dirname(__file__)
+
+# test_exec_root: The root path where tests should be run.
+llvm_obj_root = getattr(config, 'llvm_obj_root', None)
+if llvm_obj_root is not None:
+ config.test_exec_root = os.path.join(llvm_obj_root, 'test')
+
+###
+
+import os
+
+# Check that the object root is known.
+if config.test_exec_root is None:
+ # Otherwise, we haven't loaded the site specific configuration (the user is
+ # probably trying to run on a test file directly, and either the site
+ # configuration hasn't been created by the build system, or we are in an
+ # out-of-tree build situation).
+
+ # Try to detect the situation where we are using an out-of-tree build by
+ # looking for 'llvm-config'.
+ #
+ # FIXME: I debated (i.e., wrote and threw away) adding logic to
+ # automagically generate the lit.site.cfg if we are in some kind of fresh
+ # build situation. This means knowing how to invoke the build system
+ # though, and I decided it was too much magic.
+
+ llvm_config = lit.util.which('llvm-config', config.environment['PATH'])
+ if not llvm_config:
+ lit.fatal('No site specific configuration available!')
+
+ # Get the source and object roots.
+ llvm_src_root = lit.util.capture(['llvm-config', '--src-root']).strip()
+ llvm_obj_root = lit.util.capture(['llvm-config', '--obj-root']).strip()
+
+ # Validate that we got a tree which points to here.
+ this_src_root = os.path.dirname(config.test_source_root)
+ if os.path.realpath(llvm_src_root) != os.path.realpath(this_src_root):
+ lit.fatal('No site specific configuration available!')
+
+ # Check that the site specific configuration exists.
+ site_cfg = os.path.join(llvm_obj_root, 'test', 'lit.site.cfg')
+ if not os.path.exists(site_cfg):
+ lit.fatal('No site specific configuration available!')
+
+ # Okay, that worked. Notify the user of the automagic, and reconfigure.
+ lit.note('using out-of-tree build at %r' % llvm_obj_root)
+ lit.load_config(config, site_cfg)
+ raise SystemExit
+
+###
+
+# Load site data from DejaGNU's site.exp.
+import re
+site_exp = {}
+# FIXME: Implement lit.site.cfg.
+for line in open(os.path.join(config.llvm_obj_root, 'test', 'site.exp')):
+ m = re.match('set ([^ ]+) "([^"]*)"', line)
+ 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.
+config.target_triple = site_exp['target_triplet']
+
+# Provide llvm_supports_target for use in local configs.
+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
+
+RunLLVMTests \[lsort \[glob -nocomplain \$srcdir/\$subdir/\*\.(.*)\]\]""",
+ re.MULTILINE)
+conditionalLibData = re.compile(r"""load_lib llvm.exp
+
+if.*\[ ?(llvm[^ ]*) ([^ ]*) ?\].*{
+ *RunLLVMTests \[lsort \[glob -nocomplain \$srcdir/\$subdir/\*\.(.*)\]\]
+\}""", re.MULTILINE)
+def on_clone(parent, cfg, for_path):
+ def addSuffixes(match):
+ if match[0] == '{' and match[-1] == '}':
+ cfg.suffixes = ['.' + s for s in match[1:-1].split(',')]
+ else:
+ cfg.suffixes = ['.' + match]
+
+ libPath = os.path.join(os.path.dirname(for_path),
+ 'dg.exp')
+ if not os.path.exists(libPath):
+ cfg.unsupported = True
+ return
+
+ # Reset unsupported, in case we inherited it.
+ cfg.unsupported = False
+ lib = open(libPath).read().strip()
+
+ # Check for a simple library.
+ m = simpleLibData.match(lib)
+ if m:
+ addSuffixes(m.group(1))
+ return
+
+ # Check for a conditional test set.
+ m = conditionalLibData.match(lib)
+ if m:
+ funcname,arg,match = m.groups()
+ addSuffixes(match)
+
+ func = globals().get(funcname)
+ if not func:
+ lit.error('unsupported predicate %r' % funcname)
+ elif not func(arg):
+ cfg.unsupported = True
+ return
+ # Otherwise, give up.
+ lit.error('unable to understand %r:\n%s' % (libPath, lib))
+
+config.on_clone = on_clone
diff --git a/utils/lit/lit/ExampleTests/ShExternal/lit.local.cfg b/utils/lit/lit/ExampleTests/ShExternal/lit.local.cfg
new file mode 100644
index 0000000..1061da6
--- /dev/null
+++ b/utils/lit/lit/ExampleTests/ShExternal/lit.local.cfg
@@ -0,0 +1,6 @@
+# -*- Python -*-
+
+config.test_format = lit.formats.ShTest(execute_external = True)
+
+config.suffixes = ['.c']
+
diff --git a/utils/lit/lit/ExampleTests/ShInternal/lit.local.cfg b/utils/lit/lit/ExampleTests/ShInternal/lit.local.cfg
new file mode 100644
index 0000000..448eaa4
--- /dev/null
+++ b/utils/lit/lit/ExampleTests/ShInternal/lit.local.cfg
@@ -0,0 +1,6 @@
+# -*- Python -*-
+
+config.test_format = lit.formats.ShTest(execute_external = False)
+
+config.suffixes = ['.c']
+
diff --git a/utils/lit/lit/ExampleTests/TclTest/lit.local.cfg b/utils/lit/lit/ExampleTests/TclTest/lit.local.cfg
new file mode 100644
index 0000000..6a37129
--- /dev/null
+++ b/utils/lit/lit/ExampleTests/TclTest/lit.local.cfg
@@ -0,0 +1,5 @@
+# -*- Python -*-
+
+config.test_format = lit.formats.TclTest()
+
+config.suffixes = ['.ll']
diff --git a/utils/lit/lit/ExampleTests/TclTest/stderr-pipe.ll b/utils/lit/lit/ExampleTests/TclTest/stderr-pipe.ll
new file mode 100644
index 0000000..6c55fe8
--- /dev/null
+++ b/utils/lit/lit/ExampleTests/TclTest/stderr-pipe.ll
@@ -0,0 +1 @@
+; RUN: gcc -### > /dev/null |& grep {gcc version}
diff --git a/utils/lit/lit/ExampleTests/TclTest/tcl-redir-1.ll b/utils/lit/lit/ExampleTests/TclTest/tcl-redir-1.ll
new file mode 100644
index 0000000..61240ba
--- /dev/null
+++ b/utils/lit/lit/ExampleTests/TclTest/tcl-redir-1.ll
@@ -0,0 +1,7 @@
+; RUN: echo 'hi' > %t.1 | echo 'hello' > %t.2
+; RUN: not grep 'hi' %t.1
+; RUN: grep 'hello' %t.2
+
+
+
+
diff --git a/utils/lit/lit/ExampleTests/fail.c b/utils/lit/lit/ExampleTests/fail.c
new file mode 100644
index 0000000..84db41a
--- /dev/null
+++ b/utils/lit/lit/ExampleTests/fail.c
@@ -0,0 +1,2 @@
+// RUN: echo 'I am some stdout'
+// RUN: false
diff --git a/utils/lit/lit/ExampleTests/lit.cfg b/utils/lit/lit/ExampleTests/lit.cfg
new file mode 100644
index 0000000..dbd574f
--- /dev/null
+++ b/utils/lit/lit/ExampleTests/lit.cfg
@@ -0,0 +1,23 @@
+# -*- Python -*-
+
+# Configuration file for the 'lit' test runner.
+
+# name: The name of this test suite.
+config.name = 'Examples'
+
+# suffixes: A list of file extensions to treat as test files.
+config.suffixes = ['.c', '.cpp', '.m', '.mm', '.ll']
+
+# testFormat: The test format to use to interpret tests.
+config.test_format = lit.formats.ShTest()
+
+# test_source_root: The path where tests are located (default is the test suite
+# root).
+config.test_source_root = None
+
+# test_exec_root: The path where tests are located (default is the test suite
+# root).
+config.test_exec_root = None
+
+# target_triple: Used by ShTest and TclTest formats for XFAIL checks.
+config.target_triple = 'foo'
diff --git a/utils/lit/lit/ExampleTests/pass.c b/utils/lit/lit/ExampleTests/pass.c
new file mode 100644
index 0000000..5c1031c
--- /dev/null
+++ b/utils/lit/lit/ExampleTests/pass.c
@@ -0,0 +1 @@
+// RUN: true
diff --git a/utils/lit/lit/ExampleTests/xfail.c b/utils/lit/lit/ExampleTests/xfail.c
new file mode 100644
index 0000000..b36cd99
--- /dev/null
+++ b/utils/lit/lit/ExampleTests/xfail.c
@@ -0,0 +1,2 @@
+// RUN: false
+// XFAIL: *
diff --git a/utils/lit/lit/ExampleTests/xpass.c b/utils/lit/lit/ExampleTests/xpass.c
new file mode 100644
index 0000000..ad84990
--- /dev/null
+++ b/utils/lit/lit/ExampleTests/xpass.c
@@ -0,0 +1,2 @@
+// RUN: true
+// XFAIL
diff --git a/utils/lit/lit/LitConfig.py b/utils/lit/lit/LitConfig.py
new file mode 100644
index 0000000..0e0a493
--- /dev/null
+++ b/utils/lit/lit/LitConfig.py
@@ -0,0 +1,95 @@
+class LitConfig:
+ """LitConfig - Configuration data for a 'lit' test runner instance, shared
+ across all tests.
+
+ The LitConfig object is also used to communicate with client configuration
+ files, it is always passed in as the global variable 'lit' so that
+ configuration files can access common functionality and internal components
+ easily.
+ """
+
+ # Provide access to built-in formats.
+ import LitFormats as formats
+
+ # Provide access to built-in utility functions.
+ import Util as util
+
+ def __init__(self, progname, path, quiet,
+ useValgrind, valgrindArgs,
+ useTclAsSh,
+ noExecute, debug, isWindows,
+ params):
+ # The name of the test runner.
+ self.progname = progname
+ # The items to add to the PATH environment variable.
+ self.path = list(map(str, path))
+ self.quiet = bool(quiet)
+ self.useValgrind = bool(useValgrind)
+ self.valgrindArgs = list(valgrindArgs)
+ self.useTclAsSh = bool(useTclAsSh)
+ self.noExecute = noExecute
+ self.debug = debug
+ self.isWindows = bool(isWindows)
+ self.params = dict(params)
+ self.bashPath = None
+
+ self.numErrors = 0
+ self.numWarnings = 0
+
+ def load_config(self, config, path):
+ """load_config(config, path) - Load a config object from an alternate
+ path."""
+ from TestingConfig import TestingConfig
+ return TestingConfig.frompath(path, config.parent, self,
+ mustExist = True,
+ config = config)
+
+ def getBashPath(self):
+ """getBashPath - Get the path to 'bash'"""
+ import os, Util
+
+ if self.bashPath is not None:
+ return self.bashPath
+
+ self.bashPath = Util.which('bash', os.pathsep.join(self.path))
+ if self.bashPath is None:
+ # Check some known paths.
+ for path in ('/bin/bash', '/usr/bin/bash'):
+ if os.path.exists(path):
+ self.bashPath = path
+ break
+
+ if self.bashPath is None:
+ self.warning("Unable to find 'bash', running Tcl tests internally.")
+ self.bashPath = ''
+
+ return self.bashPath
+
+ def _write_message(self, kind, message):
+ import inspect, os, sys
+
+ # Get the file/line where this message was generated.
+ f = inspect.currentframe()
+ # Step out of _write_message, and then out of wrapper.
+ f = f.f_back.f_back
+ file,line,_,_,_ = inspect.getframeinfo(f)
+ location = '%s:%d' % (os.path.basename(file), line)
+
+ print >>sys.stderr, '%s: %s: %s: %s' % (self.progname, location,
+ kind, message)
+
+ def note(self, message):
+ self._write_message('note', message)
+
+ def warning(self, message):
+ self._write_message('warning', message)
+ self.numWarnings += 1
+
+ def error(self, message):
+ self._write_message('error', message)
+ self.numErrors += 1
+
+ def fatal(self, message):
+ import sys
+ self._write_message('fatal', message)
+ sys.exit(2)
diff --git a/utils/lit/lit/LitFormats.py b/utils/lit/lit/LitFormats.py
new file mode 100644
index 0000000..270f087
--- /dev/null
+++ b/utils/lit/lit/LitFormats.py
@@ -0,0 +1,3 @@
+from TestFormats import GoogleTest, ShTest, TclTest
+from TestFormats import SyntaxCheckTest, OneCommandPerFileTest
+
diff --git a/utils/lit/lit/ProgressBar.py b/utils/lit/lit/ProgressBar.py
new file mode 100644
index 0000000..85c95f5
--- /dev/null
+++ b/utils/lit/lit/ProgressBar.py
@@ -0,0 +1,267 @@
+#!/usr/bin/env python
+
+# Source: http://code.activestate.com/recipes/475116/, with
+# modifications by Daniel Dunbar.
+
+import sys, re, time
+
+class TerminalController:
+ """
+ A class that can be used to portably generate formatted output to
+ a terminal.
+
+ `TerminalController` defines a set of instance variables whose
+ values are initialized to the control sequence necessary to
+ perform a given action. These can be simply included in normal
+ output to the terminal:
+
+ >>> term = TerminalController()
+ >>> print 'This is '+term.GREEN+'green'+term.NORMAL
+
+ Alternatively, the `render()` method can used, which replaces
+ '${action}' with the string required to perform 'action':
+
+ >>> term = TerminalController()
+ >>> print term.render('This is ${GREEN}green${NORMAL}')
+
+ If the terminal doesn't support a given action, then the value of
+ the corresponding instance variable will be set to ''. As a
+ result, the above code will still work on terminals that do not
+ support color, except that their output will not be colored.
+ Also, this means that you can test whether the terminal supports a
+ given action by simply testing the truth value of the
+ corresponding instance variable:
+
+ >>> term = TerminalController()
+ >>> if term.CLEAR_SCREEN:
+ ... print 'This terminal supports clearning the screen.'
+
+ Finally, if the width and height of the terminal are known, then
+ they will be stored in the `COLS` and `LINES` attributes.
+ """
+ # Cursor movement:
+ BOL = '' #: Move the cursor to the beginning of the line
+ UP = '' #: Move the cursor up one line
+ DOWN = '' #: Move the cursor down one line
+ LEFT = '' #: Move the cursor left one char
+ RIGHT = '' #: Move the cursor right one char
+
+ # Deletion:
+ CLEAR_SCREEN = '' #: Clear the screen and move to home position
+ CLEAR_EOL = '' #: Clear to the end of the line.
+ CLEAR_BOL = '' #: Clear to the beginning of the line.
+ CLEAR_EOS = '' #: Clear to the end of the screen
+
+ # Output modes:
+ BOLD = '' #: Turn on bold mode
+ BLINK = '' #: Turn on blink mode
+ DIM = '' #: Turn on half-bright mode
+ REVERSE = '' #: Turn on reverse-video mode
+ NORMAL = '' #: Turn off all modes
+
+ # Cursor display:
+ HIDE_CURSOR = '' #: Make the cursor invisible
+ SHOW_CURSOR = '' #: Make the cursor visible
+
+ # Terminal size:
+ COLS = None #: Width of the terminal (None for unknown)
+ LINES = None #: Height of the terminal (None for unknown)
+
+ # Foreground colors:
+ BLACK = BLUE = GREEN = CYAN = RED = MAGENTA = YELLOW = WHITE = ''
+
+ # Background colors:
+ BG_BLACK = BG_BLUE = BG_GREEN = BG_CYAN = ''
+ BG_RED = BG_MAGENTA = BG_YELLOW = BG_WHITE = ''
+
+ _STRING_CAPABILITIES = """
+ BOL=cr UP=cuu1 DOWN=cud1 LEFT=cub1 RIGHT=cuf1
+ CLEAR_SCREEN=clear CLEAR_EOL=el CLEAR_BOL=el1 CLEAR_EOS=ed BOLD=bold
+ BLINK=blink DIM=dim REVERSE=rev UNDERLINE=smul NORMAL=sgr0
+ HIDE_CURSOR=cinvis SHOW_CURSOR=cnorm""".split()
+ _COLORS = """BLACK BLUE GREEN CYAN RED MAGENTA YELLOW WHITE""".split()
+ _ANSICOLORS = "BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE".split()
+
+ def __init__(self, term_stream=sys.stdout):
+ """
+ Create a `TerminalController` and initialize its attributes
+ with appropriate values for the current terminal.
+ `term_stream` is the stream that will be used for terminal
+ output; if this stream is not a tty, then the terminal is
+ assumed to be a dumb terminal (i.e., have no capabilities).
+ """
+ # Curses isn't available on all platforms
+ try: import curses
+ except: return
+
+ # If the stream isn't a tty, then assume it has no capabilities.
+ if not term_stream.isatty(): return
+
+ # Check the terminal type. If we fail, then assume that the
+ # terminal has no capabilities.
+ try: curses.setupterm()
+ except: return
+
+ # Look up numeric capabilities.
+ self.COLS = curses.tigetnum('cols')
+ self.LINES = curses.tigetnum('lines')
+
+ # Look up string capabilities.
+ for capability in self._STRING_CAPABILITIES:
+ (attrib, cap_name) = capability.split('=')
+ setattr(self, attrib, self._tigetstr(cap_name) or '')
+
+ # Colors
+ set_fg = self._tigetstr('setf')
+ if set_fg:
+ for i,color in zip(range(len(self._COLORS)), self._COLORS):
+ setattr(self, color, curses.tparm(set_fg, i) or '')
+ set_fg_ansi = self._tigetstr('setaf')
+ if set_fg_ansi:
+ for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS):
+ setattr(self, color, curses.tparm(set_fg_ansi, i) or '')
+ set_bg = self._tigetstr('setb')
+ if set_bg:
+ for i,color in zip(range(len(self._COLORS)), self._COLORS):
+ setattr(self, 'BG_'+color, curses.tparm(set_bg, i) or '')
+ set_bg_ansi = self._tigetstr('setab')
+ if set_bg_ansi:
+ for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS):
+ setattr(self, 'BG_'+color, curses.tparm(set_bg_ansi, i) or '')
+
+ def _tigetstr(self, cap_name):
+ # String capabilities can include "delays" of the form "$<2>".
+ # For any modern terminal, we should be able to just ignore
+ # these, so strip them out.
+ import curses
+ cap = curses.tigetstr(cap_name) or ''
+ return re.sub(r'\$<\d+>[/*]?', '', cap)
+
+ def render(self, template):
+ """
+ Replace each $-substitutions in the given template string with
+ the corresponding terminal control string (if it's defined) or
+ '' (if it's not).
+ """
+ return re.sub(r'\$\$|\${\w+}', self._render_sub, template)
+
+ def _render_sub(self, match):
+ s = match.group()
+ if s == '$$': return s
+ else: return getattr(self, s[2:-1])
+
+#######################################################################
+# Example use case: progress bar
+#######################################################################
+
+class SimpleProgressBar:
+ """
+ A simple progress bar which doesn't need any terminal support.
+
+ This prints out a progress bar like:
+ 'Header: 0 .. 10.. 20.. ...'
+ """
+
+ def __init__(self, header):
+ self.header = header
+ self.atIndex = None
+
+ def update(self, percent, message):
+ if self.atIndex is None:
+ sys.stdout.write(self.header)
+ self.atIndex = 0
+
+ next = int(percent*50)
+ if next == self.atIndex:
+ return
+
+ for i in range(self.atIndex, next):
+ idx = i % 5
+ if idx == 0:
+ sys.stdout.write('%-2d' % (i*2))
+ elif idx == 1:
+ pass # Skip second char
+ elif idx < 4:
+ sys.stdout.write('.')
+ else:
+ sys.stdout.write(' ')
+ sys.stdout.flush()
+ self.atIndex = next
+
+ def clear(self):
+ if self.atIndex is not None:
+ sys.stdout.write('\n')
+ sys.stdout.flush()
+ self.atIndex = None
+
+class ProgressBar:
+ """
+ A 3-line progress bar, which looks like::
+
+ Header
+ 20% [===========----------------------------------]
+ progress message
+
+ The progress bar is colored, if the terminal supports color
+ output; and adjusts to the width of the terminal.
+ """
+ BAR = '%s${GREEN}[${BOLD}%s%s${NORMAL}${GREEN}]${NORMAL}%s\n'
+ HEADER = '${BOLD}${CYAN}%s${NORMAL}\n\n'
+
+ def __init__(self, term, header, useETA=True):
+ self.term = term
+ if not (self.term.CLEAR_EOL and self.term.UP and self.term.BOL):
+ raise ValueError("Terminal isn't capable enough -- you "
+ "should use a simpler progress dispaly.")
+ self.width = self.term.COLS or 75
+ self.bar = term.render(self.BAR)
+ self.header = self.term.render(self.HEADER % header.center(self.width))
+ self.cleared = 1 #: true if we haven't drawn the bar yet.
+ self.useETA = useETA
+ if self.useETA:
+ self.startTime = time.time()
+ self.update(0, '')
+
+ def update(self, percent, message):
+ if self.cleared:
+ sys.stdout.write(self.header)
+ self.cleared = 0
+ prefix = '%3d%% ' % (percent*100,)
+ suffix = ''
+ if self.useETA:
+ elapsed = time.time() - self.startTime
+ if percent > .0001 and elapsed > 1:
+ total = elapsed / percent
+ eta = int(total - elapsed)
+ h = eta//3600.
+ m = (eta//60) % 60
+ s = eta % 60
+ suffix = ' ETA: %02d:%02d:%02d'%(h,m,s)
+ barWidth = self.width - len(prefix) - len(suffix) - 2
+ n = int(barWidth*percent)
+ if len(message) < self.width:
+ message = message + ' '*(self.width - len(message))
+ else:
+ message = '... ' + message[-(self.width-4):]
+ sys.stdout.write(
+ self.term.BOL + self.term.UP + self.term.CLEAR_EOL +
+ (self.bar % (prefix, '='*n, '-'*(barWidth-n), suffix)) +
+ self.term.CLEAR_EOL + message)
+
+ def clear(self):
+ if not self.cleared:
+ sys.stdout.write(self.term.BOL + self.term.CLEAR_EOL +
+ self.term.UP + self.term.CLEAR_EOL +
+ self.term.UP + self.term.CLEAR_EOL)
+ self.cleared = 1
+
+def test():
+ import time
+ tc = TerminalController()
+ p = ProgressBar(tc, 'Tests')
+ for i in range(101):
+ p.update(i/100., str(i))
+ time.sleep(.3)
+
+if __name__=='__main__':
+ test()
diff --git a/utils/lit/lit/ShCommands.py b/utils/lit/lit/ShCommands.py
new file mode 100644
index 0000000..4550437
--- /dev/null
+++ b/utils/lit/lit/ShCommands.py
@@ -0,0 +1,85 @@
+class Command:
+ def __init__(self, args, redirects):
+ self.args = list(args)
+ self.redirects = list(redirects)
+
+ def __repr__(self):
+ return 'Command(%r, %r)' % (self.args, self.redirects)
+
+ def __cmp__(self, other):
+ if not isinstance(other, Command):
+ return -1
+
+ return cmp((self.args, self.redirects),
+ (other.args, other.redirects))
+
+ def toShell(self, file):
+ for arg in self.args:
+ if "'" not in arg:
+ quoted = "'%s'" % arg
+ elif '"' not in arg and '$' not in arg:
+ quoted = '"%s"' % arg
+ else:
+ raise NotImplementedError,'Unable to quote %r' % arg
+ print >>file, quoted,
+
+ # For debugging / validation.
+ import ShUtil
+ dequoted = list(ShUtil.ShLexer(quoted).lex())
+ if dequoted != [arg]:
+ raise NotImplementedError,'Unable to quote %r' % arg
+
+ for r in self.redirects:
+ if len(r[0]) == 1:
+ print >>file, "%s '%s'" % (r[0][0], r[1]),
+ else:
+ print >>file, "%s%s '%s'" % (r[0][1], r[0][0], r[1]),
+
+class Pipeline:
+ def __init__(self, commands, negate=False, pipe_err=False):
+ self.commands = commands
+ self.negate = negate
+ self.pipe_err = pipe_err
+
+ def __repr__(self):
+ return 'Pipeline(%r, %r, %r)' % (self.commands, self.negate,
+ self.pipe_err)
+
+ def __cmp__(self, other):
+ if not isinstance(other, Pipeline):
+ return -1
+
+ return cmp((self.commands, self.negate, self.pipe_err),
+ (other.commands, other.negate, self.pipe_err))
+
+ def toShell(self, file, pipefail=False):
+ if pipefail != self.pipe_err:
+ raise ValueError,'Inconsistent "pipefail" attribute!'
+ if self.negate:
+ print >>file, '!',
+ for cmd in self.commands:
+ cmd.toShell(file)
+ if cmd is not self.commands[-1]:
+ print >>file, '|\n ',
+
+class Seq:
+ def __init__(self, lhs, op, rhs):
+ assert op in (';', '&', '||', '&&')
+ self.op = op
+ self.lhs = lhs
+ self.rhs = rhs
+
+ def __repr__(self):
+ return 'Seq(%r, %r, %r)' % (self.lhs, self.op, self.rhs)
+
+ def __cmp__(self, other):
+ if not isinstance(other, Seq):
+ return -1
+
+ return cmp((self.lhs, self.op, self.rhs),
+ (other.lhs, other.op, other.rhs))
+
+ def toShell(self, file, pipefail=False):
+ self.lhs.toShell(file, pipefail)
+ print >>file, ' %s\n' % self.op
+ self.rhs.toShell(file, pipefail)
diff --git a/utils/lit/lit/ShUtil.py b/utils/lit/lit/ShUtil.py
new file mode 100644
index 0000000..c4bbb3d
--- /dev/null
+++ b/utils/lit/lit/ShUtil.py
@@ -0,0 +1,346 @@
+import itertools
+
+import Util
+from ShCommands import Command, Pipeline, Seq
+
+class ShLexer:
+ def __init__(self, data, win32Escapes = False):
+ self.data = data
+ self.pos = 0
+ self.end = len(data)
+ self.win32Escapes = win32Escapes
+
+ def eat(self):
+ c = self.data[self.pos]
+ self.pos += 1
+ return c
+
+ def look(self):
+ return self.data[self.pos]
+
+ def maybe_eat(self, c):
+ """
+ maybe_eat(c) - Consume the character c if it is the next character,
+ returning True if a character was consumed. """
+ if self.data[self.pos] == c:
+ self.pos += 1
+ return True
+ return False
+
+ def lex_arg_fast(self, c):
+ # Get the leading whitespace free section.
+ chunk = self.data[self.pos - 1:].split(None, 1)[0]
+
+ # If it has special characters, the fast path failed.
+ if ('|' in chunk or '&' in chunk or
+ '<' in chunk or '>' in chunk or
+ "'" in chunk or '"' in chunk or
+ '\\' in chunk):
+ return None
+
+ self.pos = self.pos - 1 + len(chunk)
+ return chunk
+
+ def lex_arg_slow(self, c):
+ if c in "'\"":
+ str = self.lex_arg_quoted(c)
+ else:
+ str = c
+ while self.pos != self.end:
+ c = self.look()
+ if c.isspace() or c in "|&":
+ break
+ elif c in '><':
+ # This is an annoying case; we treat '2>' as a single token so
+ # we don't have to track whitespace tokens.
+
+ # If the parse string isn't an integer, do the usual thing.
+ if not str.isdigit():
+ break
+
+ # Otherwise, lex the operator and convert to a redirection
+ # token.
+ num = int(str)
+ tok = self.lex_one_token()
+ assert isinstance(tok, tuple) and len(tok) == 1
+ return (tok[0], num)
+ elif c == '"':
+ self.eat()
+ str += self.lex_arg_quoted('"')
+ elif not self.win32Escapes and c == '\\':
+ # Outside of a string, '\\' escapes everything.
+ self.eat()
+ if self.pos == self.end:
+ Util.warning("escape at end of quoted argument in: %r" %
+ self.data)
+ return str
+ str += self.eat()
+ else:
+ str += self.eat()
+ return str
+
+ def lex_arg_quoted(self, delim):
+ str = ''
+ while self.pos != self.end:
+ c = self.eat()
+ if c == delim:
+ return str
+ elif c == '\\' and delim == '"':
+ # Inside a '"' quoted string, '\\' only escapes the quote
+ # character and backslash, otherwise it is preserved.
+ if self.pos == self.end:
+ Util.warning("escape at end of quoted argument in: %r" %
+ self.data)
+ return str
+ c = self.eat()
+ if c == '"': #
+ str += '"'
+ elif c == '\\':
+ str += '\\'
+ else:
+ str += '\\' + c
+ else:
+ str += c
+ Util.warning("missing quote character in %r" % self.data)
+ return str
+
+ def lex_arg_checked(self, c):
+ pos = self.pos
+ res = self.lex_arg_fast(c)
+ end = self.pos
+
+ self.pos = pos
+ reference = self.lex_arg_slow(c)
+ if res is not None:
+ if res != reference:
+ raise ValueError,"Fast path failure: %r != %r" % (res, reference)
+ if self.pos != end:
+ raise ValueError,"Fast path failure: %r != %r" % (self.pos, end)
+ return reference
+
+ def lex_arg(self, c):
+ return self.lex_arg_fast(c) or self.lex_arg_slow(c)
+
+ def lex_one_token(self):
+ """
+ lex_one_token - Lex a single 'sh' token. """
+
+ c = self.eat()
+ if c in ';!':
+ return (c,)
+ if c == '|':
+ if self.maybe_eat('|'):
+ return ('||',)
+ return (c,)
+ if c == '&':
+ if self.maybe_eat('&'):
+ return ('&&',)
+ if self.maybe_eat('>'):
+ return ('&>',)
+ return (c,)
+ if c == '>':
+ if self.maybe_eat('&'):
+ return ('>&',)
+ if self.maybe_eat('>'):
+ return ('>>',)
+ return (c,)
+ if c == '<':
+ if self.maybe_eat('&'):
+ return ('<&',)
+ if self.maybe_eat('>'):
+ return ('<<',)
+ return (c,)
+
+ return self.lex_arg(c)
+
+ def lex(self):
+ while self.pos != self.end:
+ if self.look().isspace():
+ self.eat()
+ else:
+ yield self.lex_one_token()
+
+###
+
+class ShParser:
+ def __init__(self, data, win32Escapes = False):
+ self.data = data
+ self.tokens = ShLexer(data, win32Escapes = win32Escapes).lex()
+
+ def lex(self):
+ try:
+ return self.tokens.next()
+ except StopIteration:
+ return None
+
+ def look(self):
+ next = self.lex()
+ if next is not None:
+ self.tokens = itertools.chain([next], self.tokens)
+ return next
+
+ def parse_command(self):
+ tok = self.lex()
+ if not tok:
+ raise ValueError,"empty command!"
+ if isinstance(tok, tuple):
+ raise ValueError,"syntax error near unexpected token %r" % tok[0]
+
+ args = [tok]
+ redirects = []
+ while 1:
+ tok = self.look()
+
+ # EOF?
+ if tok is None:
+ break
+
+ # If this is an argument, just add it to the current command.
+ if isinstance(tok, str):
+ args.append(self.lex())
+ continue
+
+ # Otherwise see if it is a terminator.
+ assert isinstance(tok, tuple)
+ if tok[0] in ('|',';','&','||','&&'):
+ break
+
+ # Otherwise it must be a redirection.
+ op = self.lex()
+ arg = self.lex()
+ if not arg:
+ raise ValueError,"syntax error near token %r" % op[0]
+ redirects.append((op, arg))
+
+ return Command(args, redirects)
+
+ def parse_pipeline(self):
+ negate = False
+ if self.look() == ('!',):
+ self.lex()
+ negate = True
+
+ commands = [self.parse_command()]
+ while self.look() == ('|',):
+ self.lex()
+ commands.append(self.parse_command())
+ return Pipeline(commands, negate)
+
+ def parse(self):
+ lhs = self.parse_pipeline()
+
+ while self.look():
+ operator = self.lex()
+ assert isinstance(operator, tuple) and len(operator) == 1
+
+ if not self.look():
+ raise ValueError, "missing argument to operator %r" % operator[0]
+
+ # FIXME: Operator precedence!!
+ lhs = Seq(lhs, operator[0], self.parse_pipeline())
+
+ return lhs
+
+###
+
+import unittest
+
+class TestShLexer(unittest.TestCase):
+ def lex(self, str, *args, **kwargs):
+ return list(ShLexer(str, *args, **kwargs).lex())
+
+ def test_basic(self):
+ self.assertEqual(self.lex('a|b>c&d<e'),
+ ['a', ('|',), 'b', ('>',), 'c', ('&',), 'd',
+ ('<',), 'e'])
+
+ def test_redirection_tokens(self):
+ self.assertEqual(self.lex('a2>c'),
+ ['a2', ('>',), 'c'])
+ self.assertEqual(self.lex('a 2>c'),
+ ['a', ('>',2), 'c'])
+
+ def test_quoting(self):
+ self.assertEqual(self.lex(""" 'a' """),
+ ['a'])
+ self.assertEqual(self.lex(""" "hello\\"world" """),
+ ['hello"world'])
+ self.assertEqual(self.lex(""" "hello\\'world" """),
+ ["hello\\'world"])
+ self.assertEqual(self.lex(""" "hello\\\\world" """),
+ ["hello\\world"])
+ self.assertEqual(self.lex(""" he"llo wo"rld """),
+ ["hello world"])
+ self.assertEqual(self.lex(""" a\\ b a\\\\b """),
+ ["a b", "a\\b"])
+ self.assertEqual(self.lex(""" "" "" """),
+ ["", ""])
+ self.assertEqual(self.lex(""" a\\ b """, win32Escapes = True),
+ ['a\\', 'b'])
+
+class TestShParse(unittest.TestCase):
+ def parse(self, str):
+ return ShParser(str).parse()
+
+ def test_basic(self):
+ self.assertEqual(self.parse('echo hello'),
+ Pipeline([Command(['echo', 'hello'], [])], False))
+ self.assertEqual(self.parse('echo ""'),
+ Pipeline([Command(['echo', ''], [])], False))
+
+ def test_redirection(self):
+ self.assertEqual(self.parse('echo hello > c'),
+ Pipeline([Command(['echo', 'hello'],
+ [((('>'),), 'c')])], False))
+ self.assertEqual(self.parse('echo hello > c >> d'),
+ Pipeline([Command(['echo', 'hello'], [(('>',), 'c'),
+ (('>>',), 'd')])], False))
+ self.assertEqual(self.parse('a 2>&1'),
+ Pipeline([Command(['a'], [(('>&',2), '1')])], False))
+
+ def test_pipeline(self):
+ self.assertEqual(self.parse('a | b'),
+ Pipeline([Command(['a'], []),
+ Command(['b'], [])],
+ False))
+
+ self.assertEqual(self.parse('a | b | c'),
+ Pipeline([Command(['a'], []),
+ Command(['b'], []),
+ Command(['c'], [])],
+ False))
+
+ self.assertEqual(self.parse('! a'),
+ Pipeline([Command(['a'], [])],
+ True))
+
+ def test_list(self):
+ self.assertEqual(self.parse('a ; b'),
+ Seq(Pipeline([Command(['a'], [])], False),
+ ';',
+ Pipeline([Command(['b'], [])], False)))
+
+ self.assertEqual(self.parse('a & b'),
+ Seq(Pipeline([Command(['a'], [])], False),
+ '&',
+ Pipeline([Command(['b'], [])], False)))
+
+ self.assertEqual(self.parse('a && b'),
+ Seq(Pipeline([Command(['a'], [])], False),
+ '&&',
+ Pipeline([Command(['b'], [])], False)))
+
+ self.assertEqual(self.parse('a || b'),
+ Seq(Pipeline([Command(['a'], [])], False),
+ '||',
+ Pipeline([Command(['b'], [])], False)))
+
+ self.assertEqual(self.parse('a && b || c'),
+ Seq(Seq(Pipeline([Command(['a'], [])], False),
+ '&&',
+ Pipeline([Command(['b'], [])], False)),
+ '||',
+ Pipeline([Command(['c'], [])], False)))
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/utils/lit/lit/TclUtil.py b/utils/lit/lit/TclUtil.py
new file mode 100644
index 0000000..4a3f345
--- /dev/null
+++ b/utils/lit/lit/TclUtil.py
@@ -0,0 +1,322 @@
+import itertools
+
+from ShCommands import Command, Pipeline
+
+def tcl_preprocess(data):
+ # Tcl has a preprocessing step to replace escaped newlines.
+ i = data.find('\\\n')
+ if i == -1:
+ return data
+
+ # Replace '\\\n' and subsequent whitespace by a single space.
+ n = len(data)
+ str = data[:i]
+ i += 2
+ while i < n and data[i] in ' \t':
+ i += 1
+ return str + ' ' + data[i:]
+
+class TclLexer:
+ """TclLexer - Lex a string into "words", following the Tcl syntax."""
+
+ def __init__(self, data):
+ self.data = tcl_preprocess(data)
+ self.pos = 0
+ self.end = len(self.data)
+
+ def at_end(self):
+ return self.pos == self.end
+
+ def eat(self):
+ c = self.data[self.pos]
+ self.pos += 1
+ return c
+
+ def look(self):
+ return self.data[self.pos]
+
+ def maybe_eat(self, c):
+ """
+ maybe_eat(c) - Consume the character c if it is the next character,
+ returning True if a character was consumed. """
+ if self.data[self.pos] == c:
+ self.pos += 1
+ return True
+ return False
+
+ def escape(self, c):
+ if c == 'a':
+ return '\x07'
+ elif c == 'b':
+ return '\x08'
+ elif c == 'f':
+ return '\x0c'
+ elif c == 'n':
+ return '\n'
+ elif c == 'r':
+ return '\r'
+ elif c == 't':
+ return '\t'
+ elif c == 'v':
+ return '\x0b'
+ elif c in 'uxo':
+ raise ValueError,'Invalid quoted character %r' % c
+ else:
+ return c
+
+ def lex_braced(self):
+ # Lex until whitespace or end of string, the opening brace has already
+ # been consumed.
+
+ str = ''
+ while 1:
+ if self.at_end():
+ raise ValueError,"Unterminated '{' quoted word"
+
+ c = self.eat()
+ if c == '}':
+ break
+ elif c == '{':
+ str += '{' + self.lex_braced() + '}'
+ elif c == '\\' and self.look() in '{}':
+ str += self.eat()
+ else:
+ str += c
+
+ return str
+
+ def lex_quoted(self):
+ str = ''
+
+ while 1:
+ if self.at_end():
+ raise ValueError,"Unterminated '\"' quoted word"
+
+ c = self.eat()
+ if c == '"':
+ break
+ elif c == '\\':
+ if self.at_end():
+ raise ValueError,'Missing quoted character'
+
+ str += self.escape(self.eat())
+ else:
+ str += c
+
+ return str
+
+ def lex_unquoted(self, process_all=False):
+ # Lex until whitespace or end of string.
+ str = ''
+ while not self.at_end():
+ if not process_all:
+ if self.look().isspace() or self.look() == ';':
+ break
+
+ c = self.eat()
+ if c == '\\':
+ if self.at_end():
+ raise ValueError,'Missing quoted character'
+
+ str += self.escape(self.eat())
+ elif c == '[':
+ raise NotImplementedError, ('Command substitution is '
+ 'not supported')
+ elif c == '$' and not self.at_end() and (self.look().isalpha() or
+ self.look() == '{'):
+ raise NotImplementedError, ('Variable substitution is '
+ 'not supported')
+ else:
+ str += c
+
+ return str
+
+ def lex_one_token(self):
+ if self.maybe_eat('"'):
+ return self.lex_quoted()
+ elif self.maybe_eat('{'):
+ # Check for argument substitution.
+ if not self.maybe_eat('*'):
+ return self.lex_braced()
+
+ if not self.maybe_eat('}'):
+ return '*' + self.lex_braced()
+
+ if self.at_end() or self.look().isspace():
+ return '*'
+
+ raise NotImplementedError, "Argument substitution is unsupported"
+ else:
+ return self.lex_unquoted()
+
+ def lex(self):
+ while not self.at_end():
+ c = self.look()
+ if c in ' \t':
+ self.eat()
+ elif c in ';\n':
+ self.eat()
+ yield (';',)
+ else:
+ yield self.lex_one_token()
+
+class TclExecCommand:
+ kRedirectPrefixes1 = ('<', '>')
+ kRedirectPrefixes2 = ('<@', '<<', '2>', '>&', '>>', '>@')
+ kRedirectPrefixes3 = ('2>@', '2>>', '>>&', '>&@')
+ kRedirectPrefixes4 = ('2>@1',)
+
+ def __init__(self, args):
+ self.args = iter(args)
+
+ def lex(self):
+ try:
+ return self.args.next()
+ except StopIteration:
+ return None
+
+ def look(self):
+ next = self.lex()
+ if next is not None:
+ self.args = itertools.chain([next], self.args)
+ return next
+
+ def parse_redirect(self, tok, length):
+ if len(tok) == length:
+ arg = self.lex()
+ if arg is None:
+ raise ValueError,'Missing argument to %r redirection' % tok
+ else:
+ tok,arg = tok[:length],tok[length:]
+
+ if tok[0] == '2':
+ op = (tok[1:],2)
+ else:
+ op = (tok,)
+ return (op, arg)
+
+ def parse_pipeline(self):
+ if self.look() is None:
+ raise ValueError,"Expected at least one argument to exec"
+
+ commands = [Command([],[])]
+ while 1:
+ arg = self.lex()
+ if arg is None:
+ break
+ elif arg == '|':
+ commands.append(Command([],[]))
+ elif arg == '|&':
+ # Write this as a redirect of stderr; it must come first because
+ # stdout may have already been redirected.
+ commands[-1].redirects.insert(0, (('>&',2),'1'))
+ commands.append(Command([],[]))
+ elif arg[:4] in TclExecCommand.kRedirectPrefixes4:
+ commands[-1].redirects.append(self.parse_redirect(arg, 4))
+ elif arg[:3] in TclExecCommand.kRedirectPrefixes3:
+ commands[-1].redirects.append(self.parse_redirect(arg, 3))
+ elif arg[:2] in TclExecCommand.kRedirectPrefixes2:
+ commands[-1].redirects.append(self.parse_redirect(arg, 2))
+ elif arg[:1] in TclExecCommand.kRedirectPrefixes1:
+ commands[-1].redirects.append(self.parse_redirect(arg, 1))
+ else:
+ commands[-1].args.append(arg)
+
+ return Pipeline(commands, False, pipe_err=True)
+
+ def parse(self):
+ ignoreStderr = False
+ keepNewline = False
+
+ # Parse arguments.
+ while 1:
+ next = self.look()
+ if not isinstance(next, str) or next[0] != '-':
+ break
+
+ if next == '--':
+ self.lex()
+ break
+ elif next == '-ignorestderr':
+ ignoreStderr = True
+ elif next == '-keepnewline':
+ keepNewline = True
+ else:
+ raise ValueError,"Invalid exec argument %r" % next
+
+ return (ignoreStderr, keepNewline, self.parse_pipeline())
+
+###
+
+import unittest
+
+class TestTclLexer(unittest.TestCase):
+ def lex(self, str, *args, **kwargs):
+ return list(TclLexer(str, *args, **kwargs).lex())
+
+ def test_preprocess(self):
+ self.assertEqual(tcl_preprocess('a b'), 'a b')
+ self.assertEqual(tcl_preprocess('a\\\nb c'), 'a b c')
+
+ def test_unquoted(self):
+ self.assertEqual(self.lex('a b c'),
+ ['a', 'b', 'c'])
+ self.assertEqual(self.lex(r'a\nb\tc\ '),
+ ['a\nb\tc '])
+ self.assertEqual(self.lex(r'a \\\$b c $\\'),
+ ['a', r'\$b', 'c', '$\\'])
+
+ def test_braced(self):
+ self.assertEqual(self.lex('a {b c} {}'),
+ ['a', 'b c', ''])
+ self.assertEqual(self.lex(r'a {b {c\n}}'),
+ ['a', 'b {c\\n}'])
+ self.assertEqual(self.lex(r'a {b\{}'),
+ ['a', 'b{'])
+ self.assertEqual(self.lex(r'{*}'), ['*'])
+ self.assertEqual(self.lex(r'{*} a'), ['*', 'a'])
+ self.assertEqual(self.lex(r'{*} a'), ['*', 'a'])
+ self.assertEqual(self.lex('{a\\\n b}'),
+ ['a b'])
+
+ def test_quoted(self):
+ self.assertEqual(self.lex('a "b c"'),
+ ['a', 'b c'])
+
+ def test_terminators(self):
+ self.assertEqual(self.lex('a\nb'),
+ ['a', (';',), 'b'])
+ self.assertEqual(self.lex('a;b'),
+ ['a', (';',), 'b'])
+ self.assertEqual(self.lex('a ; b'),
+ ['a', (';',), 'b'])
+
+class TestTclExecCommand(unittest.TestCase):
+ def parse(self, str):
+ return TclExecCommand(list(TclLexer(str).lex())).parse()
+
+ def test_basic(self):
+ self.assertEqual(self.parse('echo hello'),
+ (False, False,
+ Pipeline([Command(['echo', 'hello'], [])],
+ False, True)))
+ self.assertEqual(self.parse('echo hello | grep hello'),
+ (False, False,
+ Pipeline([Command(['echo', 'hello'], []),
+ Command(['grep', 'hello'], [])],
+ False, True)))
+
+ def test_redirect(self):
+ self.assertEqual(self.parse('echo hello > a >b >>c 2> d |& e'),
+ (False, False,
+ Pipeline([Command(['echo', 'hello'],
+ [(('>&',2),'1'),
+ (('>',),'a'),
+ (('>',),'b'),
+ (('>>',),'c'),
+ (('>',2),'d')]),
+ Command(['e'], [])],
+ False, True)))
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/utils/lit/lit/Test.py b/utils/lit/lit/Test.py
new file mode 100644
index 0000000..1f6556b
--- /dev/null
+++ b/utils/lit/lit/Test.py
@@ -0,0 +1,79 @@
+import os
+
+# Test results.
+
+class TestResult:
+ def __init__(self, name, isFailure):
+ self.name = name
+ self.isFailure = isFailure
+
+PASS = TestResult('PASS', False)
+XFAIL = TestResult('XFAIL', False)
+FAIL = TestResult('FAIL', True)
+XPASS = TestResult('XPASS', True)
+UNRESOLVED = TestResult('UNRESOLVED', True)
+UNSUPPORTED = TestResult('UNSUPPORTED', False)
+
+# Test classes.
+
+class TestFormat:
+ """TestFormat - Test information provider."""
+
+ def __init__(self, name):
+ self.name = name
+
+class TestSuite:
+ """TestSuite - Information on a group of tests.
+
+ A test suite groups together a set of logically related tests.
+ """
+
+ def __init__(self, name, source_root, exec_root, config):
+ self.name = name
+ self.source_root = source_root
+ self.exec_root = exec_root
+ # The test suite configuration.
+ self.config = config
+
+ def getSourcePath(self, components):
+ return os.path.join(self.source_root, *components)
+
+ def getExecPath(self, components):
+ return os.path.join(self.exec_root, *components)
+
+class Test:
+ """Test - Information on a single test instance."""
+
+ def __init__(self, suite, path_in_suite, config):
+ self.suite = suite
+ self.path_in_suite = path_in_suite
+ self.config = config
+ # The test result code, once complete.
+ self.result = None
+ # Any additional output from the test, once complete.
+ self.output = None
+ # The wall time to execute this test, if timing and once complete.
+ self.elapsed = None
+ # The repeat index of this test, or None.
+ self.index = None
+
+ def copyWithIndex(self, index):
+ import copy
+ res = copy.copy(self)
+ res.index = index
+ return res
+
+ def setResult(self, result, output, elapsed):
+ assert self.result is None, "Test result already set!"
+ self.result = result
+ self.output = output
+ self.elapsed = elapsed
+
+ def getFullName(self):
+ return self.suite.config.name + '::' + '/'.join(self.path_in_suite)
+
+ def getSourcePath(self):
+ return self.suite.getSourcePath(self.path_in_suite)
+
+ def getExecPath(self):
+ return self.suite.getExecPath(self.path_in_suite)
diff --git a/utils/lit/lit/TestFormats.py b/utils/lit/lit/TestFormats.py
new file mode 100644
index 0000000..5dfd54a
--- /dev/null
+++ b/utils/lit/lit/TestFormats.py
@@ -0,0 +1,189 @@
+import os
+
+import Test
+import TestRunner
+import Util
+
+class GoogleTest(object):
+ def __init__(self, test_sub_dir, test_suffix):
+ self.test_sub_dir = str(test_sub_dir)
+ self.test_suffix = str(test_suffix)
+
+ def getGTestTests(self, path, litConfig):
+ """getGTestTests(path) - [name]
+
+ Return the tests available in gtest executable."""
+
+ try:
+ lines = Util.capture([path, '--gtest_list_tests']).split('\n')
+ except:
+ litConfig.error("unable to discover google-tests in %r" % path)
+ raise StopIteration
+
+ nested_tests = []
+ for ln in lines:
+ if not ln.strip():
+ continue
+
+ prefix = ''
+ index = 0
+ while ln[index*2:index*2+2] == ' ':
+ index += 1
+ while len(nested_tests) > index:
+ nested_tests.pop()
+
+ ln = ln[index*2:]
+ if ln.endswith('.'):
+ nested_tests.append(ln)
+ else:
+ yield ''.join(nested_tests) + ln
+
+ def getTestsInDirectory(self, testSuite, path_in_suite,
+ litConfig, localConfig):
+ source_path = testSuite.getSourcePath(path_in_suite)
+ for filename in os.listdir(source_path):
+ # Check for the one subdirectory (build directory) tests will be in.
+ if filename != self.test_sub_dir:
+ continue
+
+ filepath = os.path.join(source_path, filename)
+ for subfilename in os.listdir(filepath):
+ if subfilename.endswith(self.test_suffix):
+ execpath = os.path.join(filepath, subfilename)
+
+ # Discover the tests in this executable.
+ for name in self.getGTestTests(execpath, litConfig):
+ testPath = path_in_suite + (filename, subfilename, name)
+ yield Test.Test(testSuite, testPath, localConfig)
+
+ def execute(self, test, litConfig):
+ testPath,testName = os.path.split(test.getSourcePath())
+ while not os.path.exists(testPath):
+ # Handle GTest parametrized and typed tests, whose name includes
+ # some '/'s.
+ testPath, namePrefix = os.path.split(testPath)
+ testName = os.path.join(namePrefix, testName)
+
+ cmd = [testPath, '--gtest_filter=' + testName]
+ out, err, exitCode = TestRunner.executeCommand(cmd)
+
+ if not exitCode:
+ return Test.PASS,''
+
+ return Test.FAIL, out + err
+
+###
+
+class FileBasedTest(object):
+ def getTestsInDirectory(self, testSuite, path_in_suite,
+ litConfig, localConfig):
+ source_path = testSuite.getSourcePath(path_in_suite)
+ for filename in os.listdir(source_path):
+ filepath = os.path.join(source_path, filename)
+ if not os.path.isdir(filepath):
+ base,ext = os.path.splitext(filename)
+ if ext in localConfig.suffixes:
+ yield Test.Test(testSuite, path_in_suite + (filename,),
+ localConfig)
+
+class ShTest(FileBasedTest):
+ def __init__(self, execute_external = False):
+ self.execute_external = execute_external
+
+ def execute(self, test, litConfig):
+ return TestRunner.executeShTest(test, litConfig,
+ self.execute_external)
+
+class TclTest(FileBasedTest):
+ def execute(self, test, litConfig):
+ return TestRunner.executeTclTest(test, litConfig)
+
+###
+
+import re
+import tempfile
+
+class OneCommandPerFileTest:
+ # FIXME: Refactor into generic test for running some command on a directory
+ # of inputs.
+
+ def __init__(self, command, dir, recursive=False,
+ pattern=".*", useTempInput=False):
+ if isinstance(command, str):
+ self.command = [command]
+ else:
+ self.command = list(command)
+ self.dir = str(dir)
+ self.recursive = bool(recursive)
+ self.pattern = re.compile(pattern)
+ self.useTempInput = useTempInput
+
+ def getTestsInDirectory(self, testSuite, path_in_suite,
+ litConfig, localConfig):
+ for dirname,subdirs,filenames in os.walk(self.dir):
+ if not self.recursive:
+ subdirs[:] = []
+
+ subdirs[:] = [d for d in subdirs
+ if (d != '.svn' and
+ d not in localConfig.excludes)]
+
+ for filename in filenames:
+ if (not self.pattern.match(filename) or
+ filename in localConfig.excludes):
+ continue
+
+ path = os.path.join(dirname,filename)
+ suffix = path[len(self.dir):]
+ if suffix.startswith(os.sep):
+ suffix = suffix[1:]
+ test = Test.Test(testSuite,
+ path_in_suite + tuple(suffix.split(os.sep)),
+ localConfig)
+ # FIXME: Hack?
+ test.source_path = path
+ yield test
+
+ def createTempInput(self, tmp, test):
+ abstract
+
+ def execute(self, test, litConfig):
+ if test.config.unsupported:
+ return (Test.UNSUPPORTED, 'Test is unsupported')
+
+ cmd = list(self.command)
+
+ # If using temp input, create a temporary file and hand it to the
+ # subclass.
+ if self.useTempInput:
+ tmp = tempfile.NamedTemporaryFile(suffix='.cpp')
+ self.createTempInput(tmp, test)
+ tmp.flush()
+ cmd.append(tmp.name)
+ else:
+ cmd.append(test.source_path)
+
+ out, err, exitCode = TestRunner.executeCommand(cmd)
+
+ diags = out + err
+ if not exitCode and not diags.strip():
+ return Test.PASS,''
+
+ # Try to include some useful information.
+ report = """Command: %s\n""" % ' '.join(["'%s'" % a
+ for a in cmd])
+ if self.useTempInput:
+ report += """Temporary File: %s\n""" % tmp.name
+ report += "--\n%s--\n""" % open(tmp.name).read()
+ report += """Output:\n--\n%s--""" % diags
+
+ return Test.FAIL, report
+
+class SyntaxCheckTest(OneCommandPerFileTest):
+ def __init__(self, compiler, dir, extra_cxx_args=[], *args, **kwargs):
+ cmd = [compiler, '-x', 'c++', '-fsyntax-only'] + extra_cxx_args
+ OneCommandPerFileTest.__init__(self, cmd, dir,
+ useTempInput=1, *args, **kwargs)
+
+ def createTempInput(self, tmp, test):
+ print >>tmp, '#include "%s"' % test.source_path
diff --git a/utils/lit/lit/TestRunner.py b/utils/lit/lit/TestRunner.py
new file mode 100644
index 0000000..20fbc6c
--- /dev/null
+++ b/utils/lit/lit/TestRunner.py
@@ -0,0 +1,517 @@
+import os, signal, subprocess, sys
+import StringIO
+
+import ShUtil
+import Test
+import Util
+
+import platform
+import tempfile
+
+class InternalShellError(Exception):
+ def __init__(self, command, message):
+ self.command = command
+ self.message = message
+
+# Don't use close_fds on Windows.
+kUseCloseFDs = platform.system() != 'Windows'
+
+# Use temporary files to replace /dev/null on Windows.
+kAvoidDevNull = platform.system() == 'Windows'
+
+def executeCommand(command, cwd=None, env=None):
+ p = subprocess.Popen(command, cwd=cwd,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ env=env)
+ out,err = p.communicate()
+ exitCode = p.wait()
+
+ # Detect Ctrl-C in subprocess.
+ if exitCode == -signal.SIGINT:
+ raise KeyboardInterrupt
+
+ return out, err, exitCode
+
+def executeShCmd(cmd, cfg, cwd, results):
+ if isinstance(cmd, ShUtil.Seq):
+ if cmd.op == ';':
+ res = executeShCmd(cmd.lhs, cfg, cwd, results)
+ return executeShCmd(cmd.rhs, cfg, cwd, results)
+
+ if cmd.op == '&':
+ raise NotImplementedError,"unsupported test command: '&'"
+
+ if cmd.op == '||':
+ res = executeShCmd(cmd.lhs, cfg, cwd, results)
+ if res != 0:
+ res = executeShCmd(cmd.rhs, cfg, cwd, results)
+ return res
+ if cmd.op == '&&':
+ res = executeShCmd(cmd.lhs, cfg, cwd, results)
+ if res is None:
+ return res
+
+ if res == 0:
+ res = executeShCmd(cmd.rhs, cfg, cwd, results)
+ return res
+
+ raise ValueError,'Unknown shell command: %r' % cmd.op
+
+ assert isinstance(cmd, ShUtil.Pipeline)
+ procs = []
+ input = subprocess.PIPE
+ stderrTempFiles = []
+ # To avoid deadlock, we use a single stderr stream for piped
+ # output. This is null until we have seen some output using
+ # stderr.
+ for i,j in enumerate(cmd.commands):
+ # Apply the redirections, we use (N,) as a sentinal to indicate stdin,
+ # stdout, stderr for N equal to 0, 1, or 2 respectively. Redirects to or
+ # from a file are represented with a list [file, mode, file-object]
+ # where file-object is initially None.
+ redirects = [(0,), (1,), (2,)]
+ for r in j.redirects:
+ if r[0] == ('>',2):
+ redirects[2] = [r[1], 'w', None]
+ elif r[0] == ('>>',2):
+ redirects[2] = [r[1], 'a', None]
+ elif r[0] == ('>&',2) and r[1] in '012':
+ redirects[2] = redirects[int(r[1])]
+ elif r[0] == ('>&',) or r[0] == ('&>',):
+ redirects[1] = redirects[2] = [r[1], 'w', None]
+ elif r[0] == ('>',):
+ redirects[1] = [r[1], 'w', None]
+ elif r[0] == ('>>',):
+ redirects[1] = [r[1], 'a', None]
+ elif r[0] == ('<',):
+ redirects[0] = [r[1], 'r', None]
+ else:
+ raise NotImplementedError,"Unsupported redirect: %r" % (r,)
+
+ # Map from the final redirections to something subprocess can handle.
+ final_redirects = []
+ for index,r in enumerate(redirects):
+ if r == (0,):
+ result = input
+ elif r == (1,):
+ if index == 0:
+ raise NotImplementedError,"Unsupported redirect for stdin"
+ elif index == 1:
+ result = subprocess.PIPE
+ else:
+ result = subprocess.STDOUT
+ elif r == (2,):
+ if index != 2:
+ raise NotImplementedError,"Unsupported redirect on stdout"
+ result = subprocess.PIPE
+ else:
+ if r[2] is None:
+ if kAvoidDevNull and r[0] == '/dev/null':
+ r[2] = tempfile.TemporaryFile(mode=r[1])
+ else:
+ r[2] = open(r[0], r[1])
+ # Workaround a Win32 and/or subprocess bug when appending.
+ if r[1] == 'a':
+ r[2].seek(0, 2)
+ result = r[2]
+ final_redirects.append(result)
+
+ stdin, stdout, stderr = final_redirects
+
+ # If stderr wants to come from stdout, but stdout isn't a pipe, then put
+ # stderr on a pipe and treat it as stdout.
+ if (stderr == subprocess.STDOUT and stdout != subprocess.PIPE):
+ stderr = subprocess.PIPE
+ stderrIsStdout = True
+ else:
+ stderrIsStdout = False
+
+ # Don't allow stderr on a PIPE except for the last
+ # process, this could deadlock.
+ #
+ # FIXME: This is slow, but so is deadlock.
+ if stderr == subprocess.PIPE and j != cmd.commands[-1]:
+ stderr = tempfile.TemporaryFile(mode='w+b')
+ stderrTempFiles.append((i, stderr))
+
+ # Resolve the executable path ourselves.
+ args = list(j.args)
+ args[0] = Util.which(args[0], cfg.environment['PATH'])
+ if not args[0]:
+ raise InternalShellError(j, '%r: command not found' % j.args[0])
+
+ procs.append(subprocess.Popen(args, cwd=cwd,
+ stdin = stdin,
+ stdout = stdout,
+ stderr = stderr,
+ env = cfg.environment,
+ close_fds = kUseCloseFDs))
+
+ # Immediately close stdin for any process taking stdin from us.
+ if stdin == subprocess.PIPE:
+ procs[-1].stdin.close()
+ procs[-1].stdin = None
+
+ # Update the current stdin source.
+ if stdout == subprocess.PIPE:
+ input = procs[-1].stdout
+ elif stderrIsStdout:
+ input = procs[-1].stderr
+ else:
+ input = subprocess.PIPE
+
+ # FIXME: There is probably still deadlock potential here. Yawn.
+ procData = [None] * len(procs)
+ procData[-1] = procs[-1].communicate()
+
+ for i in range(len(procs) - 1):
+ if procs[i].stdout is not None:
+ out = procs[i].stdout.read()
+ else:
+ out = ''
+ if procs[i].stderr is not None:
+ err = procs[i].stderr.read()
+ else:
+ err = ''
+ procData[i] = (out,err)
+
+ # Read stderr out of the temp files.
+ for i,f in stderrTempFiles:
+ f.seek(0, 0)
+ procData[i] = (procData[i][0], f.read())
+
+ exitCode = None
+ for i,(out,err) in enumerate(procData):
+ res = procs[i].wait()
+ # Detect Ctrl-C in subprocess.
+ if res == -signal.SIGINT:
+ raise KeyboardInterrupt
+
+ results.append((cmd.commands[i], out, err, res))
+ if cmd.pipe_err:
+ # Python treats the exit code as a signed char.
+ if res < 0:
+ exitCode = min(exitCode, res)
+ else:
+ exitCode = max(exitCode, res)
+ else:
+ exitCode = res
+
+ if cmd.negate:
+ exitCode = not exitCode
+
+ return exitCode
+
+def executeScriptInternal(test, litConfig, tmpBase, commands, cwd):
+ ln = ' &&\n'.join(commands)
+ try:
+ cmd = ShUtil.ShParser(ln, litConfig.isWindows).parse()
+ except:
+ return (Test.FAIL, "shell parser error on: %r" % ln)
+
+ results = []
+ try:
+ exitCode = executeShCmd(cmd, test.config, cwd, results)
+ except InternalShellError,e:
+ out = ''
+ err = e.message
+ exitCode = 255
+
+ out = err = ''
+ for i,(cmd, cmd_out,cmd_err,res) in enumerate(results):
+ out += 'Command %d: %s\n' % (i, ' '.join('"%s"' % s for s in cmd.args))
+ out += 'Command %d Result: %r\n' % (i, res)
+ out += 'Command %d Output:\n%s\n\n' % (i, cmd_out)
+ out += 'Command %d Stderr:\n%s\n\n' % (i, cmd_err)
+
+ return out, err, exitCode
+
+def executeTclScriptInternal(test, litConfig, tmpBase, commands, cwd):
+ import TclUtil
+ cmds = []
+ for ln in commands:
+ # Given the unfortunate way LLVM's test are written, the line gets
+ # backslash substitution done twice.
+ ln = TclUtil.TclLexer(ln).lex_unquoted(process_all = True)
+
+ try:
+ tokens = list(TclUtil.TclLexer(ln).lex())
+ except:
+ return (Test.FAIL, "Tcl lexer error on: %r" % ln)
+
+ # Validate there are no control tokens.
+ for t in tokens:
+ if not isinstance(t, str):
+ return (Test.FAIL,
+ "Invalid test line: %r containing %r" % (ln, t))
+
+ try:
+ cmds.append(TclUtil.TclExecCommand(tokens).parse_pipeline())
+ except:
+ return (Test.FAIL, "Tcl 'exec' parse error on: %r" % ln)
+
+ cmd = cmds[0]
+ for c in cmds[1:]:
+ cmd = ShUtil.Seq(cmd, '&&', c)
+
+ # FIXME: This is lame, we shouldn't need bash. See PR5240.
+ bashPath = litConfig.getBashPath()
+ if litConfig.useTclAsSh and bashPath:
+ script = tmpBase + '.script'
+
+ # Write script file
+ f = open(script,'w')
+ print >>f, 'set -o pipefail'
+ cmd.toShell(f, pipefail = True)
+ f.close()
+
+ if 0:
+ print >>sys.stdout, cmd
+ print >>sys.stdout, open(script).read()
+ print >>sys.stdout
+ return '', '', 0
+
+ command = [litConfig.getBashPath(), script]
+ out,err,exitCode = executeCommand(command, cwd=cwd,
+ env=test.config.environment)
+
+ # Tcl commands fail on standard error output.
+ if err:
+ exitCode = 1
+ out = 'Command has output on stderr!\n\n' + out
+
+ return out,err,exitCode
+ else:
+ results = []
+ try:
+ exitCode = executeShCmd(cmd, test.config, cwd, results)
+ except InternalShellError,e:
+ results.append((e.command, '', e.message + '\n', 255))
+ exitCode = 255
+
+ out = err = ''
+
+ # Tcl commands fail on standard error output.
+ if [True for _,_,err,res in results if err]:
+ exitCode = 1
+ out += 'Command has output on stderr!\n\n'
+
+ for i,(cmd, cmd_out, cmd_err, res) in enumerate(results):
+ out += 'Command %d: %s\n' % (i, ' '.join('"%s"' % s for s in cmd.args))
+ out += 'Command %d Result: %r\n' % (i, res)
+ out += 'Command %d Output:\n%s\n\n' % (i, cmd_out)
+ out += 'Command %d Stderr:\n%s\n\n' % (i, cmd_err)
+
+ return out, err, exitCode
+
+def executeScript(test, litConfig, tmpBase, commands, cwd):
+ script = tmpBase + '.script'
+ if litConfig.isWindows:
+ script += '.bat'
+
+ # Write script file
+ f = open(script,'w')
+ if litConfig.isWindows:
+ f.write('\nif %ERRORLEVEL% NEQ 0 EXIT\n'.join(commands))
+ else:
+ f.write(' &&\n'.join(commands))
+ f.write('\n')
+ f.close()
+
+ if litConfig.isWindows:
+ command = ['cmd','/c', script]
+ else:
+ command = ['/bin/sh', script]
+ if litConfig.useValgrind:
+ # FIXME: Running valgrind on sh is overkill. We probably could just
+ # run on clang with no real loss.
+ valgrindArgs = ['valgrind', '-q',
+ '--tool=memcheck', '--trace-children=yes',
+ '--error-exitcode=123']
+ valgrindArgs.extend(litConfig.valgrindArgs)
+
+ command = valgrindArgs + command
+
+ return executeCommand(command, cwd=cwd, env=test.config.environment)
+
+def isExpectedFail(xfails, xtargets, target_triple):
+ # Check if any xfail matches this target.
+ for item in xfails:
+ if item == '*' or item in target_triple:
+ break
+ else:
+ return False
+
+ # If so, see if it is expected to pass on this target.
+ #
+ # FIXME: Rename XTARGET to something that makes sense, like XPASS.
+ for item in xtargets:
+ if item == '*' or item in target_triple:
+ return False
+
+ return True
+
+def parseIntegratedTestScript(test):
+ """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.
+ """
+
+ # Get the temporary location, this is always relative to the test suite
+ # root, not test source root.
+ #
+ # FIXME: This should not be here?
+ sourcepath = test.getSourcePath()
+ execpath = test.getExecPath()
+ execdir,execbase = os.path.split(execpath)
+ tmpBase = os.path.join(execdir, 'Output', execbase)
+ if test.index is not None:
+ tmpBase += '_%d' % test.index
+
+ # We use #_MARKER_# to hide %% while we do the other substitutions.
+ substitutions = [('%%', '#_MARKER_#')]
+ substitutions.extend(test.config.substitutions)
+ substitutions.extend([('%s', sourcepath),
+ ('%S', os.path.dirname(sourcepath)),
+ ('%p', os.path.dirname(sourcepath)),
+ ('%t', tmpBase + '.tmp'),
+ # FIXME: Remove this once we kill DejaGNU.
+ ('%abs_tmp', tmpBase + '.tmp'),
+ ('#_MARKER_#', '%')])
+
+ # Collect the test lines from the script.
+ script = []
+ xfails = []
+ xtargets = []
+ for ln in open(sourcepath):
+ if 'RUN:' in ln:
+ # Isolate the command to run.
+ index = ln.index('RUN:')
+ ln = ln[index+4:]
+
+ # Trim trailing whitespace.
+ ln = ln.rstrip()
+
+ # Collapse lines with trailing '\\'.
+ if script and script[-1][-1] == '\\':
+ script[-1] = script[-1][:-1] + ln
+ else:
+ script.append(ln)
+ elif 'XFAIL:' in ln:
+ items = ln[ln.index('XFAIL:') + 6:].split(',')
+ xfails.extend([s.strip() for s in items])
+ elif 'XTARGET:' in ln:
+ items = ln[ln.index('XTARGET:') + 8:].split(',')
+ xtargets.extend([s.strip() for s in items])
+ elif 'END.' in ln:
+ # Check for END. lines.
+ if ln[ln.index('END.'):].strip() == 'END.':
+ break
+
+ # Apply substitutions to the script.
+ def processLine(ln):
+ # Apply substitutions
+ for a,b in substitutions:
+ ln = ln.replace(a,b)
+
+ # Strip the trailing newline and any extra whitespace.
+ return ln.strip()
+ script = map(processLine, script)
+
+ # Verify the script contains a run line.
+ if not script:
+ return (Test.UNRESOLVED, "Test has no run line!")
+
+ if script[-1][-1] == '\\':
+ return (Test.UNRESOLVED, "Test has unterminated run lines (with '\\')")
+
+ isXFail = isExpectedFail(xfails, xtargets, test.suite.config.target_triple)
+ return script,isXFail,tmpBase,execdir
+
+def formatTestOutput(status, out, err, exitCode, script):
+ output = StringIO.StringIO()
+ print >>output, "Script:"
+ print >>output, "--"
+ print >>output, '\n'.join(script)
+ print >>output, "--"
+ print >>output, "Exit Code: %r" % exitCode
+ print >>output, "Command Output (stdout):"
+ print >>output, "--"
+ output.write(out)
+ print >>output, "--"
+ print >>output, "Command Output (stderr):"
+ print >>output, "--"
+ output.write(err)
+ print >>output, "--"
+ return (status, output.getvalue())
+
+def executeTclTest(test, litConfig):
+ if test.config.unsupported:
+ return (Test.UNSUPPORTED, 'Test is unsupported')
+
+ res = parseIntegratedTestScript(test)
+ if len(res) == 2:
+ return res
+
+ script, isXFail, tmpBase, execdir = res
+
+ if litConfig.noExecute:
+ return (Test.PASS, '')
+
+ # Create the output directory if it does not already exist.
+ Util.mkdir_p(os.path.dirname(tmpBase))
+
+ res = executeTclScriptInternal(test, litConfig, tmpBase, script, execdir)
+ if len(res) == 2:
+ return res
+
+ out,err,exitCode = res
+ if isXFail:
+ ok = exitCode != 0
+ status = (Test.XPASS, Test.XFAIL)[ok]
+ else:
+ ok = exitCode == 0
+ status = (Test.FAIL, Test.PASS)[ok]
+
+ if ok:
+ return (status,'')
+
+ return formatTestOutput(status, out, err, exitCode, script)
+
+def executeShTest(test, litConfig, useExternalSh):
+ if test.config.unsupported:
+ return (Test.UNSUPPORTED, 'Test is unsupported')
+
+ res = parseIntegratedTestScript(test)
+ if len(res) == 2:
+ return res
+
+ script, isXFail, tmpBase, execdir = res
+
+ if litConfig.noExecute:
+ return (Test.PASS, '')
+
+ # Create the output directory if it does not already exist.
+ Util.mkdir_p(os.path.dirname(tmpBase))
+
+ if useExternalSh:
+ res = executeScript(test, litConfig, tmpBase, script, execdir)
+ else:
+ res = executeScriptInternal(test, litConfig, tmpBase, script, execdir)
+ if len(res) == 2:
+ return res
+
+ out,err,exitCode = res
+ if isXFail:
+ ok = exitCode != 0
+ status = (Test.XPASS, Test.XFAIL)[ok]
+ else:
+ ok = exitCode == 0
+ status = (Test.FAIL, Test.PASS)[ok]
+
+ if ok:
+ return (status,'')
+
+ return formatTestOutput(status, out, err, exitCode, script)
diff --git a/utils/lit/lit/TestingConfig.py b/utils/lit/lit/TestingConfig.py
new file mode 100644
index 0000000..1f5067c
--- /dev/null
+++ b/utils/lit/lit/TestingConfig.py
@@ -0,0 +1,97 @@
+import os
+
+class TestingConfig:
+ """"
+ TestingConfig - Information on the tests inside a suite.
+ """
+
+ @staticmethod
+ def frompath(path, parent, litConfig, mustExist, config = None):
+ if config is None:
+ # Set the environment based on the command line arguments.
+ environment = {
+ 'PATH' : os.pathsep.join(litConfig.path +
+ [os.environ.get('PATH','')]),
+ 'PATHEXT' : os.environ.get('PATHEXT',''),
+ 'SYSTEMROOT' : os.environ.get('SYSTEMROOT',''),
+ 'LLVM_DISABLE_CRT_DEBUG' : '1',
+ }
+
+ config = TestingConfig(parent,
+ name = '<unnamed>',
+ suffixes = set(),
+ test_format = None,
+ environment = environment,
+ substitutions = [],
+ unsupported = False,
+ on_clone = None,
+ test_exec_root = None,
+ test_source_root = None,
+ excludes = [])
+
+ if os.path.exists(path):
+ # FIXME: Improve detection and error reporting of errors in the
+ # config file.
+ f = open(path)
+ cfg_globals = dict(globals())
+ cfg_globals['config'] = config
+ cfg_globals['lit'] = litConfig
+ cfg_globals['__file__'] = path
+ try:
+ exec f in cfg_globals
+ 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)
+
+ config.finish(litConfig)
+ return config
+
+ def __init__(self, parent, name, suffixes, test_format,
+ environment, substitutions, unsupported, on_clone,
+ test_exec_root, test_source_root, excludes):
+ self.parent = parent
+ self.name = str(name)
+ self.suffixes = set(suffixes)
+ self.test_format = test_format
+ self.environment = dict(environment)
+ self.substitutions = list(substitutions)
+ self.unsupported = unsupported
+ self.on_clone = on_clone
+ self.test_exec_root = test_exec_root
+ self.test_source_root = test_source_root
+ self.excludes = set(excludes)
+
+ def clone(self, path):
+ # FIXME: Chain implementations?
+ #
+ # FIXME: Allow extra parameters?
+ cfg = TestingConfig(self, self.name, self.suffixes, self.test_format,
+ self.environment, self.substitutions,
+ self.unsupported, self.on_clone,
+ self.test_exec_root, self.test_source_root,
+ self.excludes)
+ if cfg.on_clone:
+ cfg.on_clone(self, cfg, path)
+ return cfg
+
+ def finish(self, litConfig):
+ """finish() - Finish this config object, after loading is complete."""
+
+ self.name = str(self.name)
+ self.suffixes = set(self.suffixes)
+ self.environment = dict(self.environment)
+ self.substitutions = list(self.substitutions)
+ if self.test_exec_root is not None:
+ # FIXME: This should really only be suite in test suite config
+ # files. Should we distinguish them?
+ self.test_exec_root = str(self.test_exec_root)
+ if self.test_source_root is not None:
+ # FIXME: This should really only be suite in test suite config
+ # files. Should we distinguish them?
+ self.test_source_root = str(self.test_source_root)
+ self.excludes = set(self.excludes)
diff --git a/utils/lit/lit/Util.py b/utils/lit/lit/Util.py
new file mode 100644
index 0000000..66c5e46
--- /dev/null
+++ b/utils/lit/lit/Util.py
@@ -0,0 +1,124 @@
+import os, sys
+
+def detectCPUs():
+ """
+ Detects the number of CPUs on a system. Cribbed from pp.
+ """
+ # Linux, Unix and MacOS:
+ if hasattr(os, "sysconf"):
+ if os.sysconf_names.has_key("SC_NPROCESSORS_ONLN"):
+ # Linux & Unix:
+ ncpus = os.sysconf("SC_NPROCESSORS_ONLN")
+ if isinstance(ncpus, int) and ncpus > 0:
+ return ncpus
+ else: # OSX:
+ return int(os.popen2("sysctl -n hw.ncpu")[1].read())
+ # Windows:
+ if os.environ.has_key("NUMBER_OF_PROCESSORS"):
+ ncpus = int(os.environ["NUMBER_OF_PROCESSORS"])
+ if ncpus > 0:
+ return ncpus
+ return 1 # Default
+
+def mkdir_p(path):
+ """mkdir_p(path) - Make the "path" directory, if it does not exist; this
+ will also make directories for any missing parent directories."""
+ import errno
+
+ if not path or os.path.exists(path):
+ return
+
+ parent = os.path.dirname(path)
+ if parent != path:
+ mkdir_p(parent)
+
+ try:
+ os.mkdir(path)
+ except OSError,e:
+ # Ignore EEXIST, which may occur during a race condition.
+ if e.errno != errno.EEXIST:
+ raise
+
+def capture(args):
+ import subprocess
+ """capture(command) - Run the given command (or argv list) in a shell and
+ return the standard output."""
+ p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ out,_ = p.communicate()
+ return out
+
+def which(command, paths = None):
+ """which(command, [paths]) - Look up the given command in the paths string
+ (or the PATH environment variable, if unspecified)."""
+
+ if paths is None:
+ paths = os.environ.get('PATH','')
+
+ # Check for absolute match first.
+ if os.path.exists(command):
+ return command
+
+ # Would be nice if Python had a lib function for this.
+ if not paths:
+ paths = os.defpath
+
+ # Get suffixes to search.
+ pathext = os.environ.get('PATHEXT', '').split(os.pathsep)
+
+ # Search the paths...
+ for path in paths.split(os.pathsep):
+ for ext in pathext:
+ p = os.path.join(path, command + ext)
+ if os.path.exists(p):
+ return p
+
+ return None
+
+def printHistogram(items, title = 'Items'):
+ import itertools, math
+
+ items.sort(key = lambda (_,v): v)
+
+ maxValue = max([v for _,v in items])
+
+ # Select first "nice" bar height that produces more than 10 bars.
+ power = int(math.ceil(math.log(maxValue, 10)))
+ for inc in itertools.cycle((5, 2, 2.5, 1)):
+ barH = inc * 10**power
+ N = int(math.ceil(maxValue / barH))
+ if N > 10:
+ break
+ elif inc == 1:
+ power -= 1
+
+ histo = [set() for i in range(N)]
+ for name,v in items:
+ bin = min(int(N * v/maxValue), N-1)
+ histo[bin].add(name)
+
+ barW = 40
+ hr = '-' * (barW + 34)
+ print '\nSlowest %s:' % title
+ print hr
+ for name,value in items[-20:]:
+ print '%.2fs: %s' % (value, name)
+ print '\n%s Times:' % title
+ print hr
+ pDigits = int(math.ceil(math.log(maxValue, 10)))
+ pfDigits = max(0, 3-pDigits)
+ if pfDigits:
+ pDigits += pfDigits + 1
+ cDigits = int(math.ceil(math.log(len(items), 10)))
+ print "[%s] :: [%s] :: [%s]" % ('Range'.center((pDigits+1)*2 + 3),
+ 'Percentage'.center(barW),
+ 'Count'.center(cDigits*2 + 1))
+ print hr
+ for i,row in enumerate(histo):
+ pct = float(len(row)) / len(items)
+ w = int(barW * pct)
+ print "[%*.*fs,%*.*fs)" % (pDigits, pfDigits, i*barH,
+ pDigits, pfDigits, (i+1)*barH),
+ print ":: [%s%s] :: [%*d/%*d]" % ('*'*w, ' '*(barW-w),
+ cDigits, len(row),
+ cDigits, len(items))
+
diff --git a/utils/lit/lit/__init__.py b/utils/lit/lit/__init__.py
new file mode 100644
index 0000000..0102602
--- /dev/null
+++ b/utils/lit/lit/__init__.py
@@ -0,0 +1,10 @@
+"""'lit' Testing Tool"""
+
+from lit import main
+
+__author__ = 'Daniel Dunbar'
+__email__ = 'daniel@zuster.org'
+__versioninfo__ = (0, 1, 0)
+__version__ = '.'.join(map(str, __versioninfo__))
+
+__all__ = []
diff --git a/utils/lit/lit/lit.py b/utils/lit/lit/lit.py
new file mode 100755
index 0000000..f1f19c4
--- /dev/null
+++ b/utils/lit/lit/lit.py
@@ -0,0 +1,579 @@
+#!/usr/bin/env python
+
+"""
+lit - LLVM Integrated Tester.
+
+See lit.pod for more information.
+"""
+
+import math, os, platform, random, re, sys, time, threading, traceback
+
+import ProgressBar
+import TestRunner
+import Util
+
+from TestingConfig import TestingConfig
+import LitConfig
+import Test
+
+# Configuration files to look for when discovering test suites. These can be
+# overridden with --config-prefix.
+#
+# FIXME: Rename to 'config.lit', 'site.lit', and 'local.lit' ?
+gConfigName = 'lit.cfg'
+gSiteConfigName = 'lit.site.cfg'
+
+kLocalConfigName = 'lit.local.cfg'
+
+class TestingProgressDisplay:
+ def __init__(self, opts, numTests, progressBar=None):
+ self.opts = opts
+ self.numTests = numTests
+ self.current = None
+ self.lock = threading.Lock()
+ self.progressBar = progressBar
+ self.completed = 0
+
+ def update(self, test):
+ # Avoid locking overhead in quiet mode
+ if self.opts.quiet and not test.result.isFailure:
+ self.completed += 1
+ return
+
+ # Output lock.
+ self.lock.acquire()
+ try:
+ self.handleUpdate(test)
+ finally:
+ self.lock.release()
+
+ def finish(self):
+ if self.progressBar:
+ self.progressBar.clear()
+ elif self.opts.quiet:
+ pass
+ elif self.opts.succinct:
+ sys.stdout.write('\n')
+
+ def handleUpdate(self, test):
+ self.completed += 1
+ if self.progressBar:
+ self.progressBar.update(float(self.completed)/self.numTests,
+ test.getFullName())
+
+ if self.opts.succinct and not test.result.isFailure:
+ return
+
+ if self.progressBar:
+ self.progressBar.clear()
+
+ print '%s: %s (%d of %d)' % (test.result.name, test.getFullName(),
+ self.completed, self.numTests)
+
+ if test.result.isFailure and self.opts.showOutput:
+ print "%s TEST '%s' FAILED %s" % ('*'*20, test.getFullName(),
+ '*'*20)
+ print test.output
+ print "*" * 20
+
+ sys.stdout.flush()
+
+class TestProvider:
+ def __init__(self, tests, maxTime):
+ self.maxTime = maxTime
+ self.iter = iter(tests)
+ self.lock = threading.Lock()
+ self.startTime = time.time()
+
+ def get(self):
+ # Check if we have run out of time.
+ if self.maxTime is not None:
+ if time.time() - self.startTime > self.maxTime:
+ return None
+
+ # Otherwise take the next test.
+ self.lock.acquire()
+ try:
+ item = self.iter.next()
+ except StopIteration:
+ item = None
+ self.lock.release()
+ return item
+
+class Tester(threading.Thread):
+ def __init__(self, litConfig, provider, display):
+ threading.Thread.__init__(self)
+ self.litConfig = litConfig
+ self.provider = provider
+ self.display = display
+
+ def run(self):
+ while 1:
+ item = self.provider.get()
+ if item is None:
+ break
+ self.runTest(item)
+
+ def runTest(self, test):
+ result = None
+ startTime = time.time()
+ try:
+ result, output = test.config.test_format.execute(test,
+ self.litConfig)
+ except KeyboardInterrupt:
+ # This is a sad hack. Unfortunately subprocess goes
+ # bonkers with ctrl-c and we start forking merrily.
+ print '\nCtrl-C detected, goodbye.'
+ os.kill(0,9)
+ except:
+ if self.litConfig.debug:
+ raise
+ result = Test.UNRESOLVED
+ output = 'Exception during script execution:\n'
+ output += traceback.format_exc()
+ output += '\n'
+ elapsed = time.time() - startTime
+
+ test.setResult(result, output, elapsed)
+ self.display.update(test)
+
+def dirContainsTestSuite(path):
+ cfgpath = os.path.join(path, gSiteConfigName)
+ if os.path.exists(cfgpath):
+ return cfgpath
+ cfgpath = os.path.join(path, gConfigName)
+ if os.path.exists(cfgpath):
+ return cfgpath
+
+def getTestSuite(item, litConfig, cache):
+ """getTestSuite(item, litConfig, cache) -> (suite, relative_path)
+
+ Find the test suite containing @arg item.
+
+ @retval (None, ...) - Indicates no test suite contains @arg item.
+ @retval (suite, relative_path) - The suite that @arg item is in, and its
+ relative path inside that suite.
+ """
+ def search1(path):
+ # Check for a site config or a lit config.
+ cfgpath = dirContainsTestSuite(path)
+
+ # If we didn't find a config file, keep looking.
+ if not cfgpath:
+ parent,base = os.path.split(path)
+ if parent == path:
+ return (None, ())
+
+ ts, relative = search(parent)
+ return (ts, relative + (base,))
+
+ # We found a config file, load it.
+ if litConfig.debug:
+ litConfig.note('loading suite config %r' % cfgpath)
+
+ cfg = TestingConfig.frompath(cfgpath, None, litConfig, mustExist = True)
+ source_root = os.path.realpath(cfg.test_source_root or path)
+ exec_root = os.path.realpath(cfg.test_exec_root or path)
+ return Test.TestSuite(cfg.name, source_root, exec_root, cfg), ()
+
+ def search(path):
+ # Check for an already instantiated test suite.
+ res = cache.get(path)
+ if res is None:
+ cache[path] = res = search1(path)
+ return res
+
+ # Canonicalize the path.
+ item = os.path.realpath(item)
+
+ # Skip files and virtual components.
+ components = []
+ while not os.path.isdir(item):
+ parent,base = os.path.split(item)
+ if parent == item:
+ return (None, ())
+ components.append(base)
+ item = parent
+ components.reverse()
+
+ ts, relative = search(item)
+ return ts, tuple(relative + tuple(components))
+
+def getLocalConfig(ts, path_in_suite, litConfig, cache):
+ def search1(path_in_suite):
+ # Get the parent config.
+ if not path_in_suite:
+ parent = ts.config
+ else:
+ parent = search(path_in_suite[:-1])
+
+ # Load the local configuration.
+ source_path = ts.getSourcePath(path_in_suite)
+ cfgpath = os.path.join(source_path, kLocalConfigName)
+ if litConfig.debug:
+ litConfig.note('loading local config %r' % cfgpath)
+ return TestingConfig.frompath(cfgpath, parent, litConfig,
+ mustExist = False,
+ config = parent.clone(cfgpath))
+
+ def search(path_in_suite):
+ key = (ts, path_in_suite)
+ res = cache.get(key)
+ if res is None:
+ cache[key] = res = search1(path_in_suite)
+ return res
+
+ return search(path_in_suite)
+
+def getTests(path, litConfig, testSuiteCache, localConfigCache):
+ # Find the test suite for this input and its relative path.
+ ts,path_in_suite = getTestSuite(path, litConfig, testSuiteCache)
+ if ts is None:
+ litConfig.warning('unable to find test suite for %r' % path)
+ return (),()
+
+ if litConfig.debug:
+ litConfig.note('resolved input %r to %r::%r' % (path, ts.name,
+ path_in_suite))
+
+ return ts, getTestsInSuite(ts, path_in_suite, litConfig,
+ testSuiteCache, localConfigCache)
+
+def getTestsInSuite(ts, path_in_suite, litConfig,
+ testSuiteCache, localConfigCache):
+ # Check that the source path exists (errors here are reported by the
+ # caller).
+ source_path = ts.getSourcePath(path_in_suite)
+ if not os.path.exists(source_path):
+ return
+
+ # Check if the user named a test directly.
+ if not os.path.isdir(source_path):
+ lc = getLocalConfig(ts, path_in_suite[:-1], litConfig, localConfigCache)
+ yield Test.Test(ts, path_in_suite, lc)
+ return
+
+ # Otherwise we have a directory to search for tests, start by getting the
+ # local configuration.
+ lc = getLocalConfig(ts, path_in_suite, litConfig, localConfigCache)
+
+ # Search for tests.
+ for res in lc.test_format.getTestsInDirectory(ts, path_in_suite,
+ litConfig, lc):
+ yield res
+
+ # Search subdirectories.
+ for filename in os.listdir(source_path):
+ # FIXME: This doesn't belong here?
+ if filename in ('Output', '.svn') or filename in lc.excludes:
+ continue
+
+ # Ignore non-directories.
+ file_sourcepath = os.path.join(source_path, filename)
+ if not os.path.isdir(file_sourcepath):
+ continue
+
+ # Check for nested test suites, first in the execpath in case there is a
+ # site configuration and then in the source path.
+ file_execpath = ts.getExecPath(path_in_suite + (filename,))
+ if dirContainsTestSuite(file_execpath):
+ sub_ts, subiter = getTests(file_execpath, litConfig,
+ testSuiteCache, localConfigCache)
+ elif dirContainsTestSuite(file_sourcepath):
+ sub_ts, subiter = getTests(file_sourcepath, litConfig,
+ testSuiteCache, localConfigCache)
+ else:
+ # Otherwise, continue loading from inside this test suite.
+ subiter = getTestsInSuite(ts, path_in_suite + (filename,),
+ litConfig, testSuiteCache,
+ localConfigCache)
+ sub_ts = None
+
+ N = 0
+ for res in subiter:
+ N += 1
+ yield res
+ if sub_ts and not N:
+ litConfig.warning('test suite %r contained no tests' % sub_ts.name)
+
+def runTests(numThreads, litConfig, provider, display):
+ # If only using one testing thread, don't use threads at all; this lets us
+ # profile, among other things.
+ if numThreads == 1:
+ t = Tester(litConfig, provider, display)
+ t.run()
+ return
+
+ # Otherwise spin up the testing threads and wait for them to finish.
+ testers = [Tester(litConfig, provider, display)
+ for i in range(numThreads)]
+ for t in testers:
+ t.start()
+ try:
+ for t in testers:
+ t.join()
+ except KeyboardInterrupt:
+ sys.exit(2)
+
+def main():
+ # Bump the GIL check interval, its more important to get any one thread to a
+ # blocking operation (hopefully exec) than to try and unblock other threads.
+ #
+ # FIXME: This is a hack.
+ import sys
+ sys.setcheckinterval(1000)
+
+ global options
+ from optparse import OptionParser, OptionGroup
+ parser = OptionParser("usage: %prog [options] {file-or-path}")
+
+ parser.add_option("-j", "--threads", dest="numThreads", metavar="N",
+ help="Number of testing threads",
+ type=int, action="store", default=None)
+ parser.add_option("", "--config-prefix", dest="configPrefix",
+ metavar="NAME", help="Prefix for 'lit' config files",
+ action="store", default=None)
+ parser.add_option("", "--param", dest="userParameters",
+ metavar="NAME=VAL",
+ help="Add 'NAME' = 'VAL' to the user defined parameters",
+ type=str, action="append", default=[])
+
+ group = OptionGroup(parser, "Output Format")
+ # FIXME: I find these names very confusing, although I like the
+ # functionality.
+ group.add_option("-q", "--quiet", dest="quiet",
+ help="Suppress no error output",
+ action="store_true", default=False)
+ group.add_option("-s", "--succinct", dest="succinct",
+ help="Reduce amount of output",
+ action="store_true", default=False)
+ group.add_option("-v", "--verbose", dest="showOutput",
+ help="Show all test output",
+ action="store_true", default=False)
+ group.add_option("", "--no-progress-bar", dest="useProgressBar",
+ help="Do not use curses based progress bar",
+ action="store_false", default=True)
+ parser.add_option_group(group)
+
+ group = OptionGroup(parser, "Test Execution")
+ group.add_option("", "--path", dest="path",
+ help="Additional paths to add to testing environment",
+ action="append", type=str, default=[])
+ group.add_option("", "--vg", dest="useValgrind",
+ help="Run tests under valgrind",
+ action="store_true", default=False)
+ group.add_option("", "--vg-arg", dest="valgrindArgs", metavar="ARG",
+ help="Specify an extra argument for valgrind",
+ type=str, action="append", default=[])
+ group.add_option("", "--time-tests", dest="timeTests",
+ help="Track elapsed wall time for each test",
+ action="store_true", default=False)
+ group.add_option("", "--no-execute", dest="noExecute",
+ help="Don't execute any tests (assume PASS)",
+ action="store_true", default=False)
+ parser.add_option_group(group)
+
+ group = OptionGroup(parser, "Test Selection")
+ group.add_option("", "--max-tests", dest="maxTests", metavar="N",
+ help="Maximum number of tests to run",
+ action="store", type=int, default=None)
+ group.add_option("", "--max-time", dest="maxTime", metavar="N",
+ help="Maximum time to spend testing (in seconds)",
+ action="store", type=float, default=None)
+ group.add_option("", "--shuffle", dest="shuffle",
+ help="Run tests in random order",
+ action="store_true", default=False)
+ parser.add_option_group(group)
+
+ group = OptionGroup(parser, "Debug and Experimental Options")
+ group.add_option("", "--debug", dest="debug",
+ help="Enable debugging (for 'lit' development)",
+ action="store_true", default=False)
+ group.add_option("", "--show-suites", dest="showSuites",
+ help="Show discovered test suites",
+ action="store_true", default=False)
+ group.add_option("", "--no-tcl-as-sh", dest="useTclAsSh",
+ help="Don't run Tcl scripts using 'sh'",
+ action="store_false", default=True)
+ group.add_option("", "--repeat", dest="repeatTests", metavar="N",
+ help="Repeat tests N times (for timing)",
+ action="store", default=None, type=int)
+ parser.add_option_group(group)
+
+ (opts, args) = parser.parse_args()
+
+ if not args:
+ parser.error('No inputs specified')
+
+ if opts.configPrefix is not None:
+ global gConfigName, gSiteConfigName
+ gConfigName = '%s.cfg' % opts.configPrefix
+ gSiteConfigName = '%s.site.cfg' % opts.configPrefix
+
+ if opts.numThreads is None:
+ opts.numThreads = Util.detectCPUs()
+
+ inputs = args
+
+ # Create the user defined parameters.
+ userParams = {}
+ for entry in opts.userParameters:
+ if '=' not in entry:
+ name,val = entry,''
+ else:
+ name,val = entry.split('=', 1)
+ userParams[name] = val
+
+ # Create the global config object.
+ litConfig = LitConfig.LitConfig(progname = os.path.basename(sys.argv[0]),
+ path = opts.path,
+ quiet = opts.quiet,
+ useValgrind = opts.useValgrind,
+ valgrindArgs = opts.valgrindArgs,
+ useTclAsSh = opts.useTclAsSh,
+ noExecute = opts.noExecute,
+ debug = opts.debug,
+ isWindows = (platform.system()=='Windows'),
+ params = userParams)
+
+ # Load the tests from the inputs.
+ tests = []
+ testSuiteCache = {}
+ localConfigCache = {}
+ for input in inputs:
+ prev = len(tests)
+ tests.extend(getTests(input, litConfig,
+ testSuiteCache, localConfigCache)[1])
+ if prev == len(tests):
+ litConfig.warning('input %r contained no tests' % input)
+
+ # If there were any errors during test discovery, exit now.
+ if litConfig.numErrors:
+ print >>sys.stderr, '%d errors, exiting.' % litConfig.numErrors
+ sys.exit(2)
+
+ if opts.showSuites:
+ suitesAndTests = dict([(ts,[])
+ for ts,_ in testSuiteCache.values()
+ if ts])
+ for t in tests:
+ suitesAndTests[t.suite].append(t)
+
+ print '-- Test Suites --'
+ suitesAndTests = suitesAndTests.items()
+ suitesAndTests.sort(key = lambda (ts,_): ts.name)
+ for ts,ts_tests in suitesAndTests:
+ print ' %s - %d tests' %(ts.name, len(ts_tests))
+ print ' Source Root: %s' % ts.source_root
+ print ' Exec Root : %s' % ts.exec_root
+
+ # Select and order the tests.
+ numTotalTests = len(tests)
+ if opts.shuffle:
+ random.shuffle(tests)
+ else:
+ tests.sort(key = lambda t: t.getFullName())
+ if opts.maxTests is not None:
+ tests = tests[:opts.maxTests]
+
+ extra = ''
+ if len(tests) != numTotalTests:
+ extra = ' of %d' % numTotalTests
+ header = '-- Testing: %d%s tests, %d threads --'%(len(tests),extra,
+ opts.numThreads)
+
+ if opts.repeatTests:
+ tests = [t.copyWithIndex(i)
+ for t in tests
+ for i in range(opts.repeatTests)]
+
+ progressBar = None
+ if not opts.quiet:
+ if opts.succinct and opts.useProgressBar:
+ try:
+ tc = ProgressBar.TerminalController()
+ progressBar = ProgressBar.ProgressBar(tc, header)
+ except ValueError:
+ print header
+ progressBar = ProgressBar.SimpleProgressBar('Testing: ')
+ else:
+ print header
+
+ # Don't create more threads than tests.
+ opts.numThreads = min(len(tests), opts.numThreads)
+
+ startTime = time.time()
+ display = TestingProgressDisplay(opts, len(tests), progressBar)
+ provider = TestProvider(tests, opts.maxTime)
+ runTests(opts.numThreads, litConfig, provider, display)
+ display.finish()
+
+ if not opts.quiet:
+ print 'Testing Time: %.2fs'%(time.time() - startTime)
+
+ # Update results for any tests which weren't run.
+ for t in tests:
+ if t.result is None:
+ t.setResult(Test.UNRESOLVED, '', 0.0)
+
+ # List test results organized by kind.
+ hasFailures = False
+ byCode = {}
+ for t in tests:
+ if t.result not in byCode:
+ byCode[t.result] = []
+ byCode[t.result].append(t)
+ if t.result.isFailure:
+ hasFailures = True
+
+ # FIXME: Show unresolved and (optionally) unsupported tests.
+ for title,code in (('Unexpected Passing Tests', Test.XPASS),
+ ('Failing Tests', Test.FAIL)):
+ elts = byCode.get(code)
+ if not elts:
+ continue
+ print '*'*20
+ print '%s (%d):' % (title, len(elts))
+ for t in elts:
+ print ' %s' % t.getFullName()
+ print
+
+ if opts.timeTests:
+ # Collate, in case we repeated tests.
+ times = {}
+ for t in tests:
+ key = t.getFullName()
+ times[key] = times.get(key, 0.) + t.elapsed
+
+ byTime = list(times.items())
+ byTime.sort(key = lambda (name,elapsed): elapsed)
+ if byTime:
+ Util.printHistogram(byTime, title='Tests')
+
+ for name,code in (('Expected Passes ', Test.PASS),
+ ('Expected Failures ', Test.XFAIL),
+ ('Unsupported Tests ', Test.UNSUPPORTED),
+ ('Unresolved Tests ', Test.UNRESOLVED),
+ ('Unexpected Passes ', Test.XPASS),
+ ('Unexpected Failures', Test.FAIL),):
+ if opts.quiet and not code.isFailure:
+ continue
+ N = len(byCode.get(code,[]))
+ if N:
+ print ' %s: %d' % (name,N)
+
+ # If we encountered any additional errors, exit abnormally.
+ if litConfig.numErrors:
+ print >>sys.stderr, '\n%d error(s), exiting.' % litConfig.numErrors
+ sys.exit(2)
+
+ # Warn about warnings.
+ if litConfig.numWarnings:
+ print >>sys.stderr, '\n%d warning(s) in tests.' % litConfig.numWarnings
+
+ if hasFailures:
+ sys.exit(1)
+ sys.exit(0)
+
+if __name__=='__main__':
+ main()
diff --git a/utils/lit/setup.py b/utils/lit/setup.py
new file mode 100644
index 0000000..e6ae3d8
--- /dev/null
+++ b/utils/lit/setup.py
@@ -0,0 +1,69 @@
+import lit
+
+# FIXME: Support distutils?
+from setuptools import setup, find_packages
+setup(
+ name = "Lit",
+ version = lit.__version__,
+
+ author = lit.__author__,
+ author_email = lit.__email__,
+ url = 'http://llvm.org',
+ license = 'BSD',
+
+ description = "A Software Testing Tool",
+ keywords = 'test C++ automatic discovery',
+ long_description = """\
+Lit
++++
+
+About
+=====
+
+Lit is a portable tool for executing LLVM and Clang style test suites,
+summarizing their results, and providing indication of failures. Lit is designed
+to be a lightweight testing tool with as simple a user interface as possible.
+
+
+Features
+========
+
+ * Portable!
+ * Flexible test discovery.
+ * Parallel test execution.
+ * Support for multiple test formats and test suite designs.
+
+
+Documentation
+=============
+
+The offical Lit documentation is in the man page, available online in the `LLVM
+Command Guide http://llvm.org/cmds/lit.html`_.
+
+
+Source
+======
+
+The Lit source is available as part of LLVM, in the `LLVM SVN repository
+<http://llvm.org/svn/llvm-project/llvm/trunk/utils/lit`_.
+""",
+
+ classifiers=[
+ 'Development Status :: 3 - Alpha',
+ 'Environment :: Console',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: University of Illinois/NCSA Open Source License',
+ 'Natural Language :: English',
+ 'Operating System :: OS Independent',
+ 'Progamming Language :: Python',
+ 'Topic :: Software Development :: Testing',
+ ],
+
+ zip_safe = False,
+ packages = find_packages(),
+ entry_points = {
+ 'console_scripts': [
+ 'lit = lit:main',
+ ],
+ }
+)
diff --git a/utils/llvmdo b/utils/llvmdo
index 26f2183..4a7e05a 100755
--- a/utils/llvmdo
+++ b/utils/llvmdo
@@ -112,7 +112,6 @@ files_to_match="\
-o -name *.intro \
-o -name *.l \
-o -name *.ll \
- -o -name *.llx \
-o -name *.lst \
-o -name *.m4 \
-o -name *.pod \
diff --git a/utils/unittest/googletest/gtest.cc b/utils/unittest/googletest/gtest.cc
index e46e90a..b5a654f 100644
--- a/utils/unittest/googletest/gtest.cc
+++ b/utils/unittest/googletest/gtest.cc
@@ -532,7 +532,7 @@ TypeId GetTestTypeId() {
// The value of GetTestTypeId() as seen from within the Google Test
// library. This is solely for testing GetTestTypeId().
-extern const TypeId kTestTypeIdInGoogleTest = GetTestTypeId();
+const TypeId kTestTypeIdInGoogleTest = GetTestTypeId();
// This predicate-formatter checks that 'results' contains a test part
// failure of the given type and that the failure message contains the
diff --git a/utils/unittest/googletest/include/gtest/gtest-param-test.h b/utils/unittest/googletest/include/gtest/gtest-param-test.h
index 2d63237..0cf05dc 100644
--- a/utils/unittest/googletest/include/gtest/gtest-param-test.h
+++ b/utils/unittest/googletest/include/gtest/gtest-param-test.h
@@ -155,7 +155,6 @@ INSTANTIATE_TEST_CASE_P(AnotherInstantiationName, FooTest, ValuesIn(pets));
#include <gtest/internal/gtest-internal.h>
#include <gtest/internal/gtest-param-util.h>
-#include <gtest/internal/gtest-param-util-generated.h>
namespace testing {
@@ -289,6 +288,12 @@ internal::ParamGenerator<typename Container::value_type> ValuesIn(
return ValuesIn(container.begin(), container.end());
}
+} // namespace testing
+
+#include <gtest/internal/gtest-param-util-generated.h>
+
+namespace testing {
+
// Values() allows generating tests from explicitly specified list of
// parameters.
//
diff --git a/utils/unittest/googletest/tempfile.tmp b/utils/unittest/googletest/tempfile.tmp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/utils/unittest/googletest/tempfile.tmp
OpenPOWER on IntegriCloud