aboutsummaryrefslogtreecommitdiff
path: root/utils
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2015-12-30 11:46:15 +0000
committerDimitry Andric <dim@FreeBSD.org>2015-12-30 11:46:15 +0000
commitdd58ef019b700900793a1eb48b52123db01b654e (patch)
treefcfbb4df56a744f4ddc6122c50521dd3f1c5e196 /utils
parent2fe5752e3a7c345cdb59e869278d36af33c13fa4 (diff)
Notes
Diffstat (limited to 'utils')
-rw-r--r--utils/FileCheck/FileCheck.cpp44
-rw-r--r--utils/PerfectShuffle/PerfectShuffle.cpp6
-rw-r--r--utils/TableGen/AsmMatcherEmitter.cpp372
-rw-r--r--utils/TableGen/AsmWriterEmitter.cpp109
-rw-r--r--utils/TableGen/Attributes.cpp156
-rw-r--r--utils/TableGen/CMakeLists.txt1
-rw-r--r--utils/TableGen/CallingConvEmitter.cpp8
-rw-r--r--utils/TableGen/CodeGenDAGPatterns.cpp613
-rw-r--r--utils/TableGen/CodeGenDAGPatterns.h16
-rw-r--r--utils/TableGen/CodeGenInstruction.cpp1
-rw-r--r--utils/TableGen/CodeGenIntrinsics.h5
-rw-r--r--utils/TableGen/CodeGenMapTable.cpp2
-rw-r--r--utils/TableGen/CodeGenRegisters.cpp31
-rw-r--r--utils/TableGen/CodeGenSchedule.cpp91
-rw-r--r--utils/TableGen/CodeGenSchedule.h49
-rw-r--r--utils/TableGen/CodeGenTarget.cpp12
-rw-r--r--utils/TableGen/CodeGenTarget.h2
-rw-r--r--utils/TableGen/DAGISelMatcherEmitter.cpp36
-rw-r--r--utils/TableGen/DFAPacketizerEmitter.cpp704
-rw-r--r--utils/TableGen/DisassemblerEmitter.cpp5
-rw-r--r--utils/TableGen/FixedLenDecoderEmitter.cpp189
-rw-r--r--utils/TableGen/InstrInfoEmitter.cpp50
-rw-r--r--utils/TableGen/IntrinsicEmitter.cpp136
-rw-r--r--utils/TableGen/OptParserEmitter.cpp20
-rw-r--r--utils/TableGen/RegisterInfoEmitter.cpp41
-rw-r--r--utils/TableGen/SubtargetEmitter.cpp17
-rw-r--r--utils/TableGen/TableGen.cpp8
-rw-r--r--utils/TableGen/TableGenBackends.h1
-rw-r--r--utils/TableGen/X86RecognizableInstr.cpp17
-rw-r--r--utils/emacs/llvm-mode.el97
-rw-r--r--utils/lit/TODO12
-rw-r--r--utils/lit/lit/LitConfig.py36
-rw-r--r--utils/lit/lit/Test.py4
-rw-r--r--utils/lit/lit/TestRunner.py308
-rw-r--r--utils/lit/lit/TestingConfig.py2
-rw-r--r--utils/lit/lit/formats/googletest.py11
-rwxr-xr-xutils/lit/lit/main.py72
-rw-r--r--utils/lit/lit/util.py100
-rwxr-xr-xutils/lit/tests/Inputs/googletest-timeout/DummySubDir/OneTest35
-rw-r--r--utils/lit/tests/Inputs/googletest-timeout/lit.cfg9
-rw-r--r--utils/lit/tests/Inputs/shtest-timeout/infinite_loop.py10
-rw-r--r--utils/lit/tests/Inputs/shtest-timeout/lit.cfg32
-rw-r--r--utils/lit/tests/Inputs/shtest-timeout/quick_then_slow.py24
-rw-r--r--utils/lit/tests/Inputs/shtest-timeout/short.py6
-rw-r--r--utils/lit/tests/Inputs/shtest-timeout/slow.py9
-rw-r--r--utils/lit/tests/discovery.py8
-rw-r--r--utils/lit/tests/googletest-timeout.py29
-rw-r--r--utils/lit/tests/lit.cfg9
-rw-r--r--utils/lit/tests/shtest-timeout.py116
-rw-r--r--utils/llvm-build/llvmbuild/main.py98
-rwxr-xr-xutils/release/build_llvm_package.bat93
-rwxr-xr-xutils/release/merge.sh23
-rwxr-xr-xutils/release/test-release.sh77
-rw-r--r--utils/schedcover.py77
-rwxr-xr-xutils/shuffle_fuzz.py4
-rw-r--r--utils/unittest/googletest/src/gtest-internal-inl.h52
-rwxr-xr-xutils/update_llc_test_checks.py8
-rw-r--r--utils/vim/ftplugin/llvm.vim1
-rw-r--r--utils/vim/syntax/llvm.vim21
59 files changed, 2845 insertions, 1280 deletions
diff --git a/utils/FileCheck/FileCheck.cpp b/utils/FileCheck/FileCheck.cpp
index 9b9ffb049064e..f73a0fee76c82 100644
--- a/utils/FileCheck/FileCheck.cpp
+++ b/utils/FileCheck/FileCheck.cpp
@@ -399,15 +399,15 @@ size_t Pattern::Match(StringRef Buffer, size_t &MatchLen,
TmpStr = RegExStr;
unsigned InsertOffset = 0;
- for (unsigned i = 0, e = VariableUses.size(); i != e; ++i) {
+ for (const auto &VariableUse : VariableUses) {
std::string Value;
- if (VariableUses[i].first[0] == '@') {
- if (!EvaluateExpression(VariableUses[i].first, Value))
+ if (VariableUse.first[0] == '@') {
+ if (!EvaluateExpression(VariableUse.first, Value))
return StringRef::npos;
} else {
StringMap<StringRef>::iterator it =
- VariableTable.find(VariableUses[i].first);
+ VariableTable.find(VariableUse.first);
// If the variable is undefined, return an error.
if (it == VariableTable.end())
return StringRef::npos;
@@ -417,7 +417,7 @@ size_t Pattern::Match(StringRef Buffer, size_t &MatchLen,
}
// Plop it into the regex at the adjusted offset.
- TmpStr.insert(TmpStr.begin()+VariableUses[i].second+InsertOffset,
+ TmpStr.insert(TmpStr.begin() + VariableUse.second + InsertOffset,
Value.begin(), Value.end());
InsertOffset += Value.size();
}
@@ -436,11 +436,9 @@ size_t Pattern::Match(StringRef Buffer, size_t &MatchLen,
StringRef FullMatch = MatchInfo[0];
// If this defines any variables, remember their values.
- for (std::map<StringRef, unsigned>::const_iterator I = VariableDefs.begin(),
- E = VariableDefs.end();
- I != E; ++I) {
- assert(I->second < MatchInfo.size() && "Internal paren error");
- VariableTable[I->first] = MatchInfo[I->second];
+ for (const auto &VariableDef : VariableDefs) {
+ assert(VariableDef.second < MatchInfo.size() && "Internal paren error");
+ VariableTable[VariableDef.first] = MatchInfo[VariableDef.second];
}
MatchLen = FullMatch.size();
@@ -470,10 +468,10 @@ void Pattern::PrintFailureInfo(const SourceMgr &SM, StringRef Buffer,
// If this was a regular expression using variables, print the current
// variable values.
if (!VariableUses.empty()) {
- for (unsigned i = 0, e = VariableUses.size(); i != e; ++i) {
+ for (const auto &VariableUse : VariableUses) {
SmallString<256> Msg;
raw_svector_ostream OS(Msg);
- StringRef Var = VariableUses[i].first;
+ StringRef Var = VariableUse.first;
if (Var[0] == '@') {
std::string Value;
if (EvaluateExpression(Var, Value)) {
@@ -761,9 +759,7 @@ static StringRef FindFirstCandidateMatch(StringRef &Buffer,
CheckTy = Check::CheckNone;
CheckLoc = StringRef::npos;
- for (prefix_iterator I = CheckPrefixes.begin(), E = CheckPrefixes.end();
- I != E; ++I) {
- StringRef Prefix(*I);
+ for (StringRef Prefix : CheckPrefixes) {
size_t PrefixLoc = Buffer.find(Prefix);
if (PrefixLoc == StringRef::npos)
@@ -861,7 +857,7 @@ static bool ReadCheckFile(SourceMgr &SM,
for (const auto &PatternString : ImplicitCheckNot) {
// Create a buffer with fake command line content in order to display the
// command line option responsible for the specific implicit CHECK-NOT.
- std::string Prefix = std::string("-") + ImplicitCheckNot.ArgStr + "='";
+ std::string Prefix = (Twine("-") + ImplicitCheckNot.ArgStr + "='").str();
std::string Suffix = "'";
std::unique_ptr<MemoryBuffer> CmdLine = MemoryBuffer::getMemBufferCopy(
Prefix + PatternString + Suffix, "command line");
@@ -979,7 +975,7 @@ static bool ReadCheckFile(SourceMgr &SM,
return false;
}
-static void PrintCheckFailed(const SourceMgr &SM, const SMLoc &Loc,
+static void PrintCheckFailed(const SourceMgr &SM, SMLoc Loc,
const Pattern &Pat, StringRef Buffer,
StringMap<StringRef> &VariableTable) {
// Otherwise, we have an error, emit an error message.
@@ -1146,9 +1142,7 @@ bool CheckString::CheckSame(const SourceMgr &SM, StringRef Buffer) const {
bool CheckString::CheckNot(const SourceMgr &SM, StringRef Buffer,
const std::vector<const Pattern *> &NotStrings,
StringMap<StringRef> &VariableTable) const {
- for (unsigned ChunkNo = 0, e = NotStrings.size();
- ChunkNo != e; ++ChunkNo) {
- const Pattern *Pat = NotStrings[ChunkNo];
+ for (const Pattern *Pat : NotStrings) {
assert((Pat->getCheckTy() == Check::CheckNot) && "Expect CHECK-NOT!");
size_t MatchLen = 0;
@@ -1176,10 +1170,7 @@ size_t CheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
size_t LastPos = 0;
size_t StartPos = LastPos;
- for (unsigned ChunkNo = 0, e = DagNotStrings.size();
- ChunkNo != e; ++ChunkNo) {
- const Pattern &Pat = DagNotStrings[ChunkNo];
-
+ for (const Pattern &Pat : DagNotStrings) {
assert((Pat.getCheckTy() == Check::CheckDAG ||
Pat.getCheckTy() == Check::CheckNot) &&
"Invalid CHECK-DAG or CHECK-NOT!");
@@ -1253,10 +1244,7 @@ static bool ValidateCheckPrefix(StringRef CheckPrefix) {
static bool ValidateCheckPrefixes() {
StringSet<> PrefixSet;
- for (prefix_iterator I = CheckPrefixes.begin(), E = CheckPrefixes.end();
- I != E; ++I) {
- StringRef Prefix(*I);
-
+ for (StringRef Prefix : CheckPrefixes) {
// Reject empty prefixes.
if (Prefix == "")
return false;
diff --git a/utils/PerfectShuffle/PerfectShuffle.cpp b/utils/PerfectShuffle/PerfectShuffle.cpp
index f80d88563168f..5c142a5f54e16 100644
--- a/utils/PerfectShuffle/PerfectShuffle.cpp
+++ b/utils/PerfectShuffle/PerfectShuffle.cpp
@@ -85,8 +85,8 @@ static void PrintMask(unsigned i, std::ostream &OS) {
/// ShuffleVal - This represents a shufflevector operation.
struct ShuffleVal {
- unsigned Cost; // Number of instrs used to generate this value.
Operator *Op; // The Operation used to generate this value.
+ unsigned Cost; // Number of instrs used to generate this value.
unsigned short Arg0, Arg1; // Input operands for this value.
ShuffleVal() : Cost(1000000) {}
@@ -102,14 +102,14 @@ static std::vector<Operator*> TheOperators;
/// Operator - This is a vector operation that is available for use.
struct Operator {
+ const char *Name;
unsigned short ShuffleMask;
unsigned short OpNum;
- const char *Name;
unsigned Cost;
Operator(unsigned short shufflemask, const char *name, unsigned opnum,
unsigned cost = 1)
- : ShuffleMask(shufflemask), OpNum(opnum), Name(name), Cost(cost) {
+ : Name(name), ShuffleMask(shufflemask), OpNum(opnum),Cost(cost) {
TheOperators.push_back(this);
}
~Operator() {
diff --git a/utils/TableGen/AsmMatcherEmitter.cpp b/utils/TableGen/AsmMatcherEmitter.cpp
index 0d7c5ffbea1cf..4177388840bed 100644
--- a/utils/TableGen/AsmMatcherEmitter.cpp
+++ b/utils/TableGen/AsmMatcherEmitter.cpp
@@ -294,6 +294,15 @@ public:
}
};
+class AsmVariantInfo {
+public:
+ std::string RegisterPrefix;
+ std::string TokenizingCharacters;
+ std::string SeparatorCharacters;
+ std::string BreakCharacters;
+ int AsmVariantNo;
+};
+
/// MatchableInfo - Helper class for storing the necessary information for an
/// instruction or alias which is capable of being matched.
struct MatchableInfo {
@@ -460,6 +469,20 @@ struct MatchableInfo {
TheDef->getValueAsBit("UseInstAsmMatchConverter")) {
}
+ // Could remove this and the dtor if PointerUnion supported unique_ptr
+ // elements with a dynamic failure/assertion (like the one below) in the case
+ // where it was copied while being in an owning state.
+ MatchableInfo(const MatchableInfo &RHS)
+ : AsmVariantID(RHS.AsmVariantID), AsmString(RHS.AsmString),
+ TheDef(RHS.TheDef), DefRec(RHS.DefRec), ResOperands(RHS.ResOperands),
+ Mnemonic(RHS.Mnemonic), AsmOperands(RHS.AsmOperands),
+ RequiredFeatures(RHS.RequiredFeatures),
+ ConversionFnKind(RHS.ConversionFnKind),
+ HasDeprecation(RHS.HasDeprecation),
+ UseInstAsmMatchConverter(RHS.UseInstAsmMatchConverter) {
+ assert(!DefRec.is<const CodeGenInstAlias *>());
+ }
+
~MatchableInfo() {
delete DefRec.dyn_cast<const CodeGenInstAlias*>();
}
@@ -470,18 +493,12 @@ struct MatchableInfo {
void initialize(const AsmMatcherInfo &Info,
SmallPtrSetImpl<Record*> &SingletonRegisters,
- int AsmVariantNo, std::string &RegisterPrefix);
+ AsmVariantInfo const &Variant);
/// validate - Return true if this matchable is a valid thing to match against
/// and perform a bunch of validity checking.
bool validate(StringRef CommentDelimiter, bool Hack) const;
- /// extractSingletonRegisterForAsmOperand - Extract singleton register,
- /// if present, from specified token.
- void
- extractSingletonRegisterForAsmOperand(unsigned i, const AsmMatcherInfo &Info,
- std::string &RegisterPrefix);
-
/// findAsmOperand - Find the AsmOperand with the specified name and
/// suboperand index.
int findAsmOperand(StringRef N, int SubOpIdx) const {
@@ -576,8 +593,10 @@ struct MatchableInfo {
void dump() const;
private:
- void tokenizeAsmString(const AsmMatcherInfo &Info);
- void addAsmOperand(size_t Start, size_t End);
+ void tokenizeAsmString(AsmMatcherInfo const &Info,
+ AsmVariantInfo const &Variant);
+ void addAsmOperand(size_t Start, size_t End,
+ std::string const &SeparatorCharacters);
};
/// SubtargetFeatureInfo - Helper class for storing information on a subtarget
@@ -756,8 +775,7 @@ void MatchableInfo::formTwoOperandAlias(StringRef Constraint) {
// Find the ResOperand that refers to the operand we're aliasing away
// and update it to refer to the combined operand instead.
- for (unsigned i = 0, e = ResOperands.size(); i != e; ++i) {
- ResOperand &Op = ResOperands[i];
+ for (ResOperand &Op : ResOperands) {
if (Op.Kind == ResOperand::RenderAsmOperand &&
Op.AsmOperandNum == (unsigned)SrcAsmOperand) {
Op.AsmOperandNum = DstAsmOperand;
@@ -768,8 +786,7 @@ void MatchableInfo::formTwoOperandAlias(StringRef Constraint) {
AsmOperands.erase(AsmOperands.begin() + SrcAsmOperand);
// Adjust the ResOperand references to any AsmOperands that followed
// the one we just deleted.
- for (unsigned i = 0, e = ResOperands.size(); i != e; ++i) {
- ResOperand &Op = ResOperands[i];
+ for (ResOperand &Op : ResOperands) {
switch(Op.Kind) {
default:
// Nothing to do for operands that don't reference AsmOperands.
@@ -786,26 +803,58 @@ void MatchableInfo::formTwoOperandAlias(StringRef Constraint) {
}
}
+/// extractSingletonRegisterForAsmOperand - Extract singleton register,
+/// if present, from specified token.
+static void
+extractSingletonRegisterForAsmOperand(MatchableInfo::AsmOperand &Op,
+ const AsmMatcherInfo &Info,
+ StringRef RegisterPrefix) {
+ StringRef Tok = Op.Token;
+
+ // If this token is not an isolated token, i.e., it isn't separated from
+ // other tokens (e.g. with whitespace), don't interpret it as a register name.
+ if (!Op.IsIsolatedToken)
+ return;
+
+ if (RegisterPrefix.empty()) {
+ std::string LoweredTok = Tok.lower();
+ if (const CodeGenRegister *Reg = Info.Target.getRegisterByName(LoweredTok))
+ Op.SingletonReg = Reg->TheDef;
+ return;
+ }
+
+ if (!Tok.startswith(RegisterPrefix))
+ return;
+
+ StringRef RegName = Tok.substr(RegisterPrefix.size());
+ if (const CodeGenRegister *Reg = Info.Target.getRegisterByName(RegName))
+ Op.SingletonReg = Reg->TheDef;
+
+ // If there is no register prefix (i.e. "%" in "%eax"), then this may
+ // be some random non-register token, just ignore it.
+ return;
+}
+
void MatchableInfo::initialize(const AsmMatcherInfo &Info,
SmallPtrSetImpl<Record*> &SingletonRegisters,
- int AsmVariantNo, std::string &RegisterPrefix) {
- AsmVariantID = AsmVariantNo;
+ AsmVariantInfo const &Variant) {
+ AsmVariantID = Variant.AsmVariantNo;
AsmString =
- CodeGenInstruction::FlattenAsmStringVariants(AsmString, AsmVariantNo);
+ CodeGenInstruction::FlattenAsmStringVariants(AsmString,
+ Variant.AsmVariantNo);
- tokenizeAsmString(Info);
+ tokenizeAsmString(Info, Variant);
// Compute the require features.
- std::vector<Record*> Predicates =TheDef->getValueAsListOfDefs("Predicates");
- for (unsigned i = 0, e = Predicates.size(); i != e; ++i)
+ for (Record *Predicate : TheDef->getValueAsListOfDefs("Predicates"))
if (const SubtargetFeatureInfo *Feature =
- Info.getSubtargetFeature(Predicates[i]))
+ Info.getSubtargetFeature(Predicate))
RequiredFeatures.push_back(Feature);
// Collect singleton registers, if used.
- for (unsigned i = 0, e = AsmOperands.size(); i != e; ++i) {
- extractSingletonRegisterForAsmOperand(i, Info, RegisterPrefix);
- if (Record *Reg = AsmOperands[i].SingletonReg)
+ for (MatchableInfo::AsmOperand &Op : AsmOperands) {
+ extractSingletonRegisterForAsmOperand(Op, Info, Variant.RegisterPrefix);
+ if (Record *Reg = Op.SingletonReg)
SingletonRegisters.insert(Reg);
}
@@ -818,9 +867,9 @@ void MatchableInfo::initialize(const AsmMatcherInfo &Info,
}
/// Append an AsmOperand for the given substring of AsmString.
-void MatchableInfo::addAsmOperand(size_t Start, size_t End) {
+void MatchableInfo::addAsmOperand(size_t Start, size_t End,
+ std::string const &Separators) {
StringRef String = AsmString;
- StringRef Separators = "[]*! \t,";
// Look for separators before and after to figure out is this token is
// isolated. Accept '$$' as that's how we escape '$'.
bool IsIsolatedToken =
@@ -831,92 +880,86 @@ void MatchableInfo::addAsmOperand(size_t Start, size_t End) {
}
/// tokenizeAsmString - Tokenize a simplified assembly string.
-void MatchableInfo::tokenizeAsmString(const AsmMatcherInfo &Info) {
+void MatchableInfo::tokenizeAsmString(const AsmMatcherInfo &Info,
+ AsmVariantInfo const &Variant) {
StringRef String = AsmString;
- unsigned Prev = 0;
- bool InTok = true;
- for (unsigned i = 0, e = String.size(); i != e; ++i) {
- switch (String[i]) {
- case '[':
- case ']':
- case '*':
- case '!':
- case ' ':
- case '\t':
- case ',':
- if (InTok) {
- addAsmOperand(Prev, i);
+ size_t Prev = 0;
+ bool InTok = false;
+ std::string Separators = Variant.TokenizingCharacters +
+ Variant.SeparatorCharacters;
+ for (size_t i = 0, e = String.size(); i != e; ++i) {
+ if(Variant.BreakCharacters.find(String[i]) != std::string::npos) {
+ if(InTok) {
+ addAsmOperand(Prev, i, Separators);
+ Prev = i;
+ }
+ InTok = true;
+ continue;
+ }
+ if(Variant.TokenizingCharacters.find(String[i]) != std::string::npos) {
+ if(InTok) {
+ addAsmOperand(Prev, i, Separators);
InTok = false;
}
- if (!isspace(String[i]) && String[i] != ',')
- addAsmOperand(i, i + 1);
+ addAsmOperand(i, i + 1, Separators);
Prev = i + 1;
- break;
-
+ continue;
+ }
+ if(Variant.SeparatorCharacters.find(String[i]) != std::string::npos) {
+ if(InTok) {
+ addAsmOperand(Prev, i, Separators);
+ InTok = false;
+ }
+ Prev = i + 1;
+ continue;
+ }
+ switch (String[i]) {
case '\\':
if (InTok) {
- addAsmOperand(Prev, i);
+ addAsmOperand(Prev, i, Separators);
InTok = false;
}
++i;
assert(i != String.size() && "Invalid quoted character");
- addAsmOperand(i, i + 1);
+ addAsmOperand(i, i + 1, Separators);
Prev = i + 1;
break;
case '$': {
- if (InTok) {
- addAsmOperand(Prev, i);
+ if (InTok && Prev != i) {
+ addAsmOperand(Prev, i, Separators);
InTok = false;
}
- // If this isn't "${", treat like a normal token.
+ // If this isn't "${", start new identifier looking like "$xxx"
if (i + 1 == String.size() || String[i + 1] != '{') {
Prev = i;
break;
}
- StringRef::iterator End = std::find(String.begin() + i, String.end(),'}');
- assert(End != String.end() && "Missing brace in operand reference!");
- size_t EndPos = End - String.begin();
- addAsmOperand(i, EndPos+1);
+ size_t EndPos = String.find('}', i);
+ assert(EndPos != StringRef::npos &&
+ "Missing brace in operand reference!");
+ addAsmOperand(i, EndPos+1, Separators);
Prev = EndPos + 1;
i = EndPos;
break;
}
-
- case '.':
- if (!Info.AsmParser->getValueAsBit("MnemonicContainsDot")) {
- if (InTok)
- addAsmOperand(Prev, i);
- Prev = i;
- }
- InTok = true;
- break;
-
default:
InTok = true;
}
}
if (InTok && Prev != String.size())
- addAsmOperand(Prev, StringRef::npos);
+ addAsmOperand(Prev, StringRef::npos, Separators);
// The first token of the instruction is the mnemonic, which must be a
// simple string, not a $foo variable or a singleton register.
if (AsmOperands.empty())
PrintFatalError(TheDef->getLoc(),
"Instruction '" + TheDef->getName() + "' has no tokens");
- Mnemonic = AsmOperands[0].Token;
- if (Mnemonic.empty())
- PrintFatalError(TheDef->getLoc(),
- "Missing instruction mnemonic");
- // FIXME : Check and raise an error if it is a register.
- if (Mnemonic[0] == '$')
- PrintFatalError(TheDef->getLoc(),
- "Invalid instruction mnemonic '" + Mnemonic + "'!");
-
- // Remove the first operand, it is tracked in the mnemonic field.
- AsmOperands.erase(AsmOperands.begin());
+ assert(!AsmOperands[0].Token.empty());
+ if (AsmOperands[0].Token[0] != '$')
+ Mnemonic = AsmOperands[0].Token;
}
bool MatchableInfo::validate(StringRef CommentDelimiter, bool Hack) const {
@@ -946,8 +989,8 @@ bool MatchableInfo::validate(StringRef CommentDelimiter, bool Hack) const {
// Also, check for instructions which reference the operand multiple times;
// this implies a constraint we would not honor.
std::set<std::string> OperandNames;
- for (unsigned i = 0, e = AsmOperands.size(); i != e; ++i) {
- StringRef Tok = AsmOperands[i].Token;
+ for (const AsmOperand &Op : AsmOperands) {
+ StringRef Tok = Op.Token;
if (Tok[0] == '$' && Tok.find(':') != StringRef::npos)
PrintFatalError(TheDef->getLoc(),
"matchable with operand modifier '" + Tok +
@@ -974,38 +1017,6 @@ bool MatchableInfo::validate(StringRef CommentDelimiter, bool Hack) const {
return true;
}
-/// extractSingletonRegisterForAsmOperand - Extract singleton register,
-/// if present, from specified token.
-void MatchableInfo::
-extractSingletonRegisterForAsmOperand(unsigned OperandNo,
- const AsmMatcherInfo &Info,
- std::string &RegisterPrefix) {
- StringRef Tok = AsmOperands[OperandNo].Token;
-
- // If this token is not an isolated token, i.e., it isn't separated from
- // other tokens (e.g. with whitespace), don't interpret it as a register name.
- if (!AsmOperands[OperandNo].IsIsolatedToken)
- return;
-
- if (RegisterPrefix.empty()) {
- std::string LoweredTok = Tok.lower();
- if (const CodeGenRegister *Reg = Info.Target.getRegisterByName(LoweredTok))
- AsmOperands[OperandNo].SingletonReg = Reg->TheDef;
- return;
- }
-
- if (!Tok.startswith(RegisterPrefix))
- return;
-
- StringRef RegName = Tok.substr(RegisterPrefix.size());
- if (const CodeGenRegister *Reg = Info.Target.getRegisterByName(RegName))
- AsmOperands[OperandNo].SingletonReg = Reg->TheDef;
-
- // If there is no register prefix (i.e. "%" in "%eax"), then this may
- // be some random non-register token, just ignore it.
- return;
-}
-
static std::string getEnumNameForToken(StringRef Str) {
std::string Res;
@@ -1364,8 +1375,15 @@ void AsmMatcherInfo::buildInfo() {
Record *AsmVariant = Target.getAsmParserVariant(VC);
std::string CommentDelimiter =
AsmVariant->getValueAsString("CommentDelimiter");
- std::string RegisterPrefix = AsmVariant->getValueAsString("RegisterPrefix");
- int AsmVariantNo = AsmVariant->getValueAsInt("Variant");
+ AsmVariantInfo Variant;
+ Variant.RegisterPrefix = AsmVariant->getValueAsString("RegisterPrefix");
+ Variant.TokenizingCharacters =
+ AsmVariant->getValueAsString("TokenizingCharacters");
+ Variant.SeparatorCharacters =
+ AsmVariant->getValueAsString("SeparatorCharacters");
+ Variant.BreakCharacters =
+ AsmVariant->getValueAsString("BreakCharacters");
+ Variant.AsmVariantNo = AsmVariant->getValueAsInt("Variant");
for (const CodeGenInstruction *CGI : Target.instructions()) {
@@ -1378,9 +1396,9 @@ void AsmMatcherInfo::buildInfo() {
if (CGI->TheDef->getValueAsBit("isCodeGenOnly"))
continue;
- std::unique_ptr<MatchableInfo> II(new MatchableInfo(*CGI));
+ auto II = llvm::make_unique<MatchableInfo>(*CGI);
- II->initialize(*this, SingletonRegisters, AsmVariantNo, RegisterPrefix);
+ II->initialize(*this, SingletonRegisters, Variant);
// Ignore instructions which shouldn't be matched and diagnose invalid
// instruction definitions with an error.
@@ -1396,7 +1414,8 @@ void AsmMatcherInfo::buildInfo() {
Records.getAllDerivedDefinitions("InstAlias");
for (unsigned i = 0, e = AllInstAliases.size(); i != e; ++i) {
auto Alias = llvm::make_unique<CodeGenInstAlias>(AllInstAliases[i],
- AsmVariantNo, Target);
+ Variant.AsmVariantNo,
+ Target);
// If the tblgen -match-prefix option is specified (for tblgen hackers),
// filter the set of instruction aliases we consider, based on the target
@@ -1405,9 +1424,9 @@ void AsmMatcherInfo::buildInfo() {
.startswith( MatchPrefix))
continue;
- std::unique_ptr<MatchableInfo> II(new MatchableInfo(std::move(Alias)));
+ auto II = llvm::make_unique<MatchableInfo>(std::move(Alias));
- II->initialize(*this, SingletonRegisters, AsmVariantNo, RegisterPrefix);
+ II->initialize(*this, SingletonRegisters, Variant);
// Validate the alias definitions.
II->validate(CommentDelimiter, false);
@@ -1434,7 +1453,7 @@ void AsmMatcherInfo::buildInfo() {
StringRef Token = Op.Token;
// Check for singleton registers.
- if (Record *RegRecord = II->AsmOperands[i].SingletonReg) {
+ if (Record *RegRecord = Op.SingletonReg) {
Op.Class = RegisterClasses[RegRecord];
assert(Op.Class && Op.Class->Registers.size() == 1 &&
"Unexpected class for singleton register");
@@ -1474,7 +1493,7 @@ void AsmMatcherInfo::buildInfo() {
II->TheDef->getValueAsString("TwoOperandAliasConstraint");
if (Constraint != "") {
// Start by making a copy of the original matchable.
- std::unique_ptr<MatchableInfo> AliasII(new MatchableInfo(*II));
+ auto AliasII = llvm::make_unique<MatchableInfo>(*II);
// Adjust it to be a two-operand alias.
AliasII->formTwoOperandAlias(Constraint);
@@ -1494,8 +1513,7 @@ void AsmMatcherInfo::buildInfo() {
// information.
std::vector<Record*> AllTokenAliases =
Records.getAllDerivedDefinitions("TokenAlias");
- for (unsigned i = 0, e = AllTokenAliases.size(); i != e; ++i) {
- Record *Rec = AllTokenAliases[i];
+ for (Record *Rec : AllTokenAliases) {
ClassInfo *FromClass = getTokenClass(Rec->getValueAsString("FromToken"));
ClassInfo *ToClass = getTokenClass(Rec->getValueAsString("ToToken"));
if (FromClass == ToClass)
@@ -1603,9 +1621,7 @@ void MatchableInfo::buildInstructionResultOperands() {
// Loop over all operands of the result instruction, determining how to
// populate them.
- for (unsigned i = 0, e = ResultInst->Operands.size(); i != e; ++i) {
- const CGIOperandList::OperandInfo &OpInfo = ResultInst->Operands[i];
-
+ for (const CGIOperandList::OperandInfo &OpInfo : ResultInst->Operands) {
// If this is a tied operand, just copy from the previously handled operand.
int TiedOp = -1;
if (OpInfo.MINumOperands == 1)
@@ -1701,7 +1717,7 @@ void MatchableInfo::buildAliasResultOperands() {
}
static unsigned getConverterOperandID(const std::string &Name,
- SetVector<std::string> &Table,
+ SmallSetVector<std::string, 16> &Table,
bool &IsNew) {
IsNew = Table.insert(Name);
@@ -1717,8 +1733,8 @@ static unsigned getConverterOperandID(const std::string &Name,
static void emitConvertFuncs(CodeGenTarget &Target, StringRef ClassName,
std::vector<std::unique_ptr<MatchableInfo>> &Infos,
raw_ostream &OS) {
- SetVector<std::string> OperandConversionKinds;
- SetVector<std::string> InstructionConversionKinds;
+ SmallSetVector<std::string, 16> OperandConversionKinds;
+ SmallSetVector<std::string, 16> InstructionConversionKinds;
std::vector<std::vector<uint8_t> > ConversionTable;
size_t MaxRowLength = 2; // minimum is custom converter plus terminator.
@@ -1850,7 +1866,7 @@ static void emitConvertFuncs(CodeGenTarget &Target, StringRef ClassName,
// Add the operand entry to the instruction kind conversion row.
ConversionRow.push_back(ID);
- ConversionRow.push_back(OpInfo.AsmOperandNum + 1);
+ ConversionRow.push_back(OpInfo.AsmOperandNum);
if (!IsNewConverter)
break;
@@ -1959,7 +1975,7 @@ static void emitConvertFuncs(CodeGenTarget &Target, StringRef ClassName,
continue;
// Add the row to the table.
- ConversionTable.push_back(ConversionRow);
+ ConversionTable.push_back(std::move(ConversionRow));
}
// Finish up the converter driver function.
@@ -1979,10 +1995,8 @@ static void emitConvertFuncs(CodeGenTarget &Target, StringRef ClassName,
// Output the instruction conversion kind enum.
OS << "enum InstructionConversionKind {\n";
- for (SetVector<std::string>::const_iterator
- i = InstructionConversionKinds.begin(),
- e = InstructionConversionKinds.end(); i != e; ++i)
- OS << " " << *i << ",\n";
+ for (const std::string &Signature : InstructionConversionKinds)
+ OS << " " << Signature << ",\n";
OS << " CVT_NUM_SIGNATURES\n";
OS << "};\n\n";
@@ -2105,12 +2119,7 @@ static void emitIsSubclass(CodeGenTarget &Target,
OS << " if (A == B)\n";
OS << " return true;\n\n";
- std::string OStr;
- raw_string_ostream SS(OStr);
- unsigned Count = 0;
- SS << " switch (A) {\n";
- SS << " default:\n";
- SS << " return false;\n";
+ bool EmittedSwitch = false;
for (const auto &A : Infos) {
std::vector<StringRef> SuperClasses;
for (const auto &B : Infos) {
@@ -2120,33 +2129,38 @@ static void emitIsSubclass(CodeGenTarget &Target,
if (SuperClasses.empty())
continue;
- ++Count;
- SS << "\n case " << A.Name << ":\n";
+ // If this is the first SuperClass, emit the switch header.
+ if (!EmittedSwitch) {
+ OS << " switch (A) {\n";
+ OS << " default:\n";
+ OS << " return false;\n";
+ EmittedSwitch = true;
+ }
+
+ OS << "\n case " << A.Name << ":\n";
if (SuperClasses.size() == 1) {
- SS << " return B == " << SuperClasses.back().str() << ";\n";
+ OS << " return B == " << SuperClasses.back() << ";\n";
continue;
}
if (!SuperClasses.empty()) {
- SS << " switch (B) {\n";
- SS << " default: return false;\n";
- for (unsigned i = 0, e = SuperClasses.size(); i != e; ++i)
- SS << " case " << SuperClasses[i].str() << ": return true;\n";
- SS << " }\n";
+ OS << " switch (B) {\n";
+ OS << " default: return false;\n";
+ for (StringRef SC : SuperClasses)
+ OS << " case " << SC << ": return true;\n";
+ OS << " }\n";
} else {
// No case statement to emit
- SS << " return false;\n";
+ OS << " return false;\n";
}
}
- SS << " }\n";
+ OS << " }\n";
- // If there were case statements emitted into the string stream, write them
- // to the output stream, otherwise write the default.
- if (Count)
- OS << SS.str();
- else
+ // If there were case statements emitted into the string stream write the
+ // default.
+ if (!EmittedSwitch)
OS << " return false;\n";
OS << "}\n\n";
@@ -2575,13 +2589,17 @@ static void emitCustomOperandParsing(raw_ostream &OS, CodeGenTarget &Target,
OS << " uint64_t AvailableFeatures = getAvailableFeatures();\n\n";
OS << " // Get the next operand index.\n";
- OS << " unsigned NextOpNum = Operands.size()-1;\n";
+ OS << " unsigned NextOpNum = Operands.size();\n";
// Emit code to search the table.
OS << " // Search the table.\n";
OS << " std::pair<const OperandMatchEntry*, const OperandMatchEntry*>";
- OS << " MnemonicRange =\n";
- OS << " std::equal_range(OperandMatchTable, OperandMatchTable+"
+ OS << " MnemonicRange\n";
+ OS << " (OperandMatchTable, OperandMatchTable+";
+ OS << Info.OperandMatchInfo.size() << ");\n";
+ OS << " if(!Mnemonic.empty())\n";
+ OS << " MnemonicRange = std::equal_range(OperandMatchTable,";
+ OS << " OperandMatchTable+"
<< Info.OperandMatchInfo.size() << ", Mnemonic,\n"
<< " LessOpcodeOperand());\n\n";
@@ -2682,7 +2700,7 @@ void AsmMatcherEmitter::run(raw_ostream &OS) {
<< "&Operands);\n";
OS << " void convertToMapAndConstraints(unsigned Kind,\n ";
OS << " const OperandVector &Operands) override;\n";
- OS << " bool mnemonicIsValid(StringRef Mnemonic, unsigned VariantID) override;\n";
+ OS << " bool mnemonicIsValid(StringRef Mnemonic, unsigned VariantID);\n";
OS << " unsigned MatchInstructionImpl(const OperandVector &Operands,\n"
<< " MCInst &Inst,\n"
<< " uint64_t &ErrorInfo,"
@@ -2891,8 +2909,8 @@ void AsmMatcherEmitter::run(raw_ostream &OS) {
<< " bool matchingInlineAsm, unsigned VariantID) {\n";
OS << " // Eliminate obvious mismatches.\n";
- OS << " if (Operands.size() > " << (MaxNumOperands+1) << ") {\n";
- OS << " ErrorInfo = " << (MaxNumOperands+1) << ";\n";
+ OS << " if (Operands.size() > " << MaxNumOperands << ") {\n";
+ OS << " ErrorInfo = " << MaxNumOperands << ";\n";
OS << " return Match_InvalidOperand;\n";
OS << " }\n\n";
@@ -2901,7 +2919,9 @@ void AsmMatcherEmitter::run(raw_ostream &OS) {
OS << " uint64_t AvailableFeatures = getAvailableFeatures();\n\n";
OS << " // Get the instruction mnemonic, which is the first token.\n";
- OS << " StringRef Mnemonic = ((" << Target.getName()
+ OS << " StringRef Mnemonic;\n";
+ OS << " if (Operands[0]->isToken())\n";
+ OS << " Mnemonic = ((" << Target.getName()
<< "Operand&)*Operands[0]).getToken();\n\n";
if (HasMnemonicAliases) {
@@ -2932,8 +2952,11 @@ void AsmMatcherEmitter::run(raw_ostream &OS) {
}
OS << " }\n";
OS << " // Search the table.\n";
- OS << " std::pair<const MatchEntry*, const MatchEntry*> MnemonicRange =\n";
- OS << " std::equal_range(Start, End, Mnemonic, LessOpcode());\n\n";
+ OS << " std::pair<const MatchEntry*, const MatchEntry*> "
+ "MnemonicRange(Start, End);\n";
+ OS << " unsigned SIndex = Mnemonic.empty() ? 0 : 1;\n";
+ OS << " if (!Mnemonic.empty())\n";
+ OS << " MnemonicRange = std::equal_range(Start, End, Mnemonic.lower(), LessOpcode());\n\n";
OS << " // Return a more specific error code if no mnemonics match.\n";
OS << " if (MnemonicRange.first == MnemonicRange.second)\n";
@@ -2943,28 +2966,23 @@ void AsmMatcherEmitter::run(raw_ostream &OS) {
<< "*ie = MnemonicRange.second;\n";
OS << " it != ie; ++it) {\n";
- OS << " // equal_range guarantees that instruction mnemonic matches.\n";
- OS << " assert(Mnemonic == it->getMnemonic());\n";
-
// Emit check that the subclasses match.
OS << " bool OperandsValid = true;\n";
- OS << " for (unsigned i = 0; i != " << MaxNumOperands << "; ++i) {\n";
- OS << " if (i + 1 >= Operands.size()) {\n";
- OS << " OperandsValid = (it->Classes[i] == " <<"InvalidMatchClass);\n";
- OS << " if (!OperandsValid) ErrorInfo = i + 1;\n";
+ OS << " for (unsigned i = SIndex; i != " << MaxNumOperands << "; ++i) {\n";
+ OS << " auto Formal = static_cast<MatchClassKind>(it->Classes[i]);\n";
+ OS << " if (i >= Operands.size()) {\n";
+ OS << " OperandsValid = (Formal == " <<"InvalidMatchClass);\n";
+ OS << " if (!OperandsValid) ErrorInfo = i;\n";
OS << " break;\n";
OS << " }\n";
- OS << " unsigned Diag = validateOperandClass(*Operands[i+1],\n";
- OS.indent(43);
- OS << "(MatchClassKind)it->Classes[i]);\n";
+ OS << " MCParsedAsmOperand &Actual = *Operands[i];\n";
+ OS << " unsigned Diag = validateOperandClass(Actual, Formal);\n";
OS << " if (Diag == Match_Success)\n";
OS << " continue;\n";
OS << " // If the generic handler indicates an invalid operand\n";
OS << " // failure, check for a special case.\n";
OS << " if (Diag == Match_InvalidOperand) {\n";
- OS << " Diag = validateTargetOperandClass(*Operands[i+1],\n";
- OS.indent(43);
- OS << "(MatchClassKind)it->Classes[i]);\n";
+ OS << " Diag = validateTargetOperandClass(Actual, Formal);\n";
OS << " if (Diag == Match_Success)\n";
OS << " continue;\n";
OS << " }\n";
@@ -2973,8 +2991,8 @@ void AsmMatcherEmitter::run(raw_ostream &OS) {
OS << " // If we already had a match that only failed due to a\n";
OS << " // target predicate, that diagnostic is preferred.\n";
OS << " if (!HadMatchOtherThanPredicate &&\n";
- OS << " (it == MnemonicRange.first || ErrorInfo <= i+1)) {\n";
- OS << " ErrorInfo = i+1;\n";
+ OS << " (it == MnemonicRange.first || ErrorInfo <= i)) {\n";
+ OS << " ErrorInfo = i;\n";
OS << " // InvalidOperand is the default. Prefer specificity.\n";
OS << " if (Diag != Match_InvalidOperand)\n";
OS << " RetCode = Diag;\n";
@@ -3029,7 +3047,7 @@ void AsmMatcherEmitter::run(raw_ostream &OS) {
if (HasDeprecation) {
OS << " std::string Info;\n";
- OS << " if (MII.get(Inst.getOpcode()).getDeprecatedInfo(Inst, STI, Info)) {\n";
+ OS << " if (MII.get(Inst.getOpcode()).getDeprecatedInfo(Inst, getSTI(), Info)) {\n";
OS << " SMLoc Loc = ((" << Target.getName()
<< "Operand&)*Operands[0]).getStartLoc();\n";
OS << " getParser().Warning(Loc, Info, None);\n";
diff --git a/utils/TableGen/AsmWriterEmitter.cpp b/utils/TableGen/AsmWriterEmitter.cpp
index 8163f681d88da..cc74f9ebceced 100644
--- a/utils/TableGen/AsmWriterEmitter.cpp
+++ b/utils/TableGen/AsmWriterEmitter.cpp
@@ -586,6 +586,8 @@ void AsmWriterEmitter::EmitGetRegisterName(raw_ostream &O) {
const auto &Registers = Target.getRegBank().getRegisters();
std::vector<Record*> AltNameIndices = Target.getRegAltNameIndices();
bool hasAltNames = AltNameIndices.size() > 1;
+ std::string Namespace =
+ Registers.front().TheDef->getValueAsString("Namespace");
O <<
"\n\n/// getRegisterName - This method is automatically generated by tblgen\n"
@@ -610,9 +612,9 @@ void AsmWriterEmitter::EmitGetRegisterName(raw_ostream &O) {
O << " switch(AltIdx) {\n"
<< " default: llvm_unreachable(\"Invalid register alt name index!\");\n";
for (unsigned i = 0, e = AltNameIndices.size(); i < e; ++i) {
- std::string Namespace = AltNameIndices[1]->getValueAsString("Namespace");
std::string AltName(AltNameIndices[i]->getName());
- O << " case " << Namespace << "::" << AltName << ":\n"
+ std::string Prefix = !Namespace.empty() ? Namespace + "::" : "";
+ O << " case " << Prefix << AltName << ":\n"
<< " assert(*(AsmStrs" << AltName << "+RegAsmOffset"
<< AltName << "[RegNo-1]) &&\n"
<< " \"Invalid alt name index for register!\");\n"
@@ -727,7 +729,6 @@ public:
++I;
}
}
- OS.flush();
// Emit the string.
O.indent(6) << "AsmString = \"" << OutString << "\";\n";
@@ -736,14 +737,13 @@ public:
O.indent(4) << '}';
}
- bool operator==(const IAPrinter &RHS) {
+ bool operator==(const IAPrinter &RHS) const {
if (Conds.size() != RHS.Conds.size())
return false;
unsigned Idx = 0;
- for (std::vector<std::string>::iterator
- I = Conds.begin(), E = Conds.end(); I != E; ++I)
- if (*I != RHS.Conds[Idx++])
+ for (const auto &str : Conds)
+ if (str != RHS.Conds[Idx++])
return false;
return true;
@@ -762,12 +762,12 @@ static unsigned CountNumOperands(StringRef AsmString, unsigned Variant) {
namespace {
struct AliasPriorityComparator {
- typedef std::pair<CodeGenInstAlias *, int> ValueType;
+ typedef std::pair<CodeGenInstAlias, int> ValueType;
bool operator()(const ValueType &LHS, const ValueType &RHS) {
if (LHS.second == RHS.second) {
// We don't actually care about the order, but for consistency it
// shouldn't depend on pointer comparisons.
- return LHS.first->TheDef->getName() < RHS.first->TheDef->getName();
+ return LHS.first.TheDef->getName() < RHS.first.TheDef->getName();
}
// Aliases with larger priorities should be considered first.
@@ -796,12 +796,11 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) {
Records.getAllDerivedDefinitions("InstAlias");
// Create a map from the qualified name to a list of potential matches.
- typedef std::set<std::pair<CodeGenInstAlias*, int>, AliasPriorityComparator>
+ typedef std::set<std::pair<CodeGenInstAlias, int>, AliasPriorityComparator>
AliasWithPriority;
std::map<std::string, AliasWithPriority> AliasMap;
for (std::vector<Record*>::iterator
I = AllInstAliases.begin(), E = AllInstAliases.end(); I != E; ++I) {
- CodeGenInstAlias *Alias = new CodeGenInstAlias(*I, Variant, Target);
const Record *R = *I;
int Priority = R->getValueAsInt("EmitPriority");
if (Priority < 1)
@@ -809,13 +808,13 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) {
const DagInit *DI = R->getValueAsDag("ResultInst");
const DefInit *Op = cast<DefInit>(DI->getOperator());
- AliasMap[getQualifiedName(Op->getDef())].insert(std::make_pair(Alias,
- Priority));
+ AliasMap[getQualifiedName(Op->getDef())].insert(
+ std::make_pair(CodeGenInstAlias(*I, Variant, Target), Priority));
}
// A map of which conditions need to be met for each instruction operand
// before it can be matched to the mnemonic.
- std::map<std::string, std::vector<IAPrinter*> > IAPrinterMap;
+ std::map<std::string, std::vector<IAPrinter>> IAPrinterMap;
// A list of MCOperandPredicates for all operands in use, and the reverse map
std::vector<const Record*> MCOpPredicates;
@@ -823,25 +822,24 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) {
for (auto &Aliases : AliasMap) {
for (auto &Alias : Aliases.second) {
- const CodeGenInstAlias *CGA = Alias.first;
- unsigned LastOpNo = CGA->ResultInstOperandIndex.size();
+ const CodeGenInstAlias &CGA = Alias.first;
+ unsigned LastOpNo = CGA.ResultInstOperandIndex.size();
unsigned NumResultOps =
- CountNumOperands(CGA->ResultInst->AsmString, Variant);
+ CountNumOperands(CGA.ResultInst->AsmString, Variant);
// Don't emit the alias if it has more operands than what it's aliasing.
- if (NumResultOps < CountNumOperands(CGA->AsmString, Variant))
+ if (NumResultOps < CountNumOperands(CGA.AsmString, Variant))
continue;
- IAPrinter *IAP = new IAPrinter(CGA->Result->getAsString(),
- CGA->AsmString);
+ IAPrinter IAP(CGA.Result->getAsString(), CGA.AsmString);
unsigned NumMIOps = 0;
- for (auto &Operand : CGA->ResultOperands)
+ for (auto &Operand : CGA.ResultOperands)
NumMIOps += Operand.getMINumOperands();
std::string Cond;
Cond = std::string("MI->getNumOperands() == ") + llvm::utostr(NumMIOps);
- IAP->addCond(Cond);
+ IAP.addCond(Cond);
bool CantHandle = false;
@@ -849,7 +847,7 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) {
for (unsigned i = 0, e = LastOpNo; i != e; ++i) {
std::string Op = "MI->getOperand(" + llvm::utostr(MIOpNum) + ")";
- const CodeGenInstAlias::ResultOperand &RO = CGA->ResultOperands[i];
+ const CodeGenInstAlias::ResultOperand &RO = CGA.ResultOperands[i];
switch (RO.Kind) {
case CodeGenInstAlias::ResultOperand::K_Record: {
@@ -875,11 +873,11 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) {
if (Rec->isSubClassOf("RegisterOperand"))
Rec = Rec->getValueAsDef("RegClass");
if (Rec->isSubClassOf("RegisterClass")) {
- IAP->addCond(Op + ".isReg()");
+ IAP.addCond(Op + ".isReg()");
- if (!IAP->isOpMapped(ROName)) {
- IAP->addOperand(ROName, MIOpNum, PrintMethodIdx);
- Record *R = CGA->ResultOperands[i].getRecord();
+ if (!IAP.isOpMapped(ROName)) {
+ IAP.addOperand(ROName, MIOpNum, PrintMethodIdx);
+ Record *R = CGA.ResultOperands[i].getRecord();
if (R->isSubClassOf("RegisterOperand"))
R = R->getValueAsDef("RegClass");
Cond = std::string("MRI.getRegClass(") + Target.getName() + "::" +
@@ -887,12 +885,12 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) {
".contains(" + Op + ".getReg())";
} else {
Cond = Op + ".getReg() == MI->getOperand(" +
- llvm::utostr(IAP->getOpIndex(ROName)) + ").getReg()";
+ llvm::utostr(IAP.getOpIndex(ROName)) + ").getReg()";
}
} else {
// Assume all printable operands are desired for now. This can be
// overridden in the InstAlias instantiation if necessary.
- IAP->addOperand(ROName, MIOpNum, PrintMethodIdx);
+ IAP.addOperand(ROName, MIOpNum, PrintMethodIdx);
// There might be an additional predicate on the MCOperand
unsigned Entry = MCOpPredicateMap[Rec];
@@ -905,42 +903,41 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) {
break; // No conditions on this operand at all
}
Cond = Target.getName() + ClassName + "ValidateMCOperand(" +
- Op + ", " + llvm::utostr(Entry) + ")";
+ Op + ", STI, " + llvm::utostr(Entry) + ")";
}
// for all subcases of ResultOperand::K_Record:
- IAP->addCond(Cond);
+ IAP.addCond(Cond);
break;
}
case CodeGenInstAlias::ResultOperand::K_Imm: {
// Just because the alias has an immediate result, doesn't mean the
// MCInst will. An MCExpr could be present, for example.
- IAP->addCond(Op + ".isImm()");
+ IAP.addCond(Op + ".isImm()");
- Cond = Op + ".getImm() == "
- + llvm::utostr(CGA->ResultOperands[i].getImm());
- IAP->addCond(Cond);
+ Cond = Op + ".getImm() == " +
+ llvm::utostr(CGA.ResultOperands[i].getImm());
+ IAP.addCond(Cond);
break;
}
case CodeGenInstAlias::ResultOperand::K_Reg:
// If this is zero_reg, something's playing tricks we're not
// equipped to handle.
- if (!CGA->ResultOperands[i].getRegister()) {
+ if (!CGA.ResultOperands[i].getRegister()) {
CantHandle = true;
break;
}
- Cond = Op + ".getReg() == " + Target.getName() +
- "::" + CGA->ResultOperands[i].getRegister()->getName();
- IAP->addCond(Cond);
+ Cond = Op + ".getReg() == " + Target.getName() + "::" +
+ CGA.ResultOperands[i].getRegister()->getName();
+ IAP.addCond(Cond);
break;
}
- if (!IAP) break;
MIOpNum += RO.getMINumOperands();
}
if (CantHandle) continue;
- IAPrinterMap[Aliases.first].push_back(IAP);
+ IAPrinterMap[Aliases.first].push_back(std::move(IAP));
}
}
@@ -959,30 +956,26 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) {
std::string Cases;
raw_string_ostream CasesO(Cases);
- for (std::map<std::string, std::vector<IAPrinter*> >::iterator
- I = IAPrinterMap.begin(), E = IAPrinterMap.end(); I != E; ++I) {
- std::vector<IAPrinter*> &IAPs = I->second;
+ for (auto &Entry : IAPrinterMap) {
+ std::vector<IAPrinter> &IAPs = Entry.second;
std::vector<IAPrinter*> UniqueIAPs;
- for (std::vector<IAPrinter*>::iterator
- II = IAPs.begin(), IE = IAPs.end(); II != IE; ++II) {
- IAPrinter *LHS = *II;
+ for (auto &LHS : IAPs) {
bool IsDup = false;
- for (std::vector<IAPrinter*>::iterator
- III = IAPs.begin(), IIE = IAPs.end(); III != IIE; ++III) {
- IAPrinter *RHS = *III;
- if (LHS != RHS && *LHS == *RHS) {
+ for (const auto &RHS : IAPs) {
+ if (&LHS != &RHS && LHS == RHS) {
IsDup = true;
break;
}
}
- if (!IsDup) UniqueIAPs.push_back(LHS);
+ if (!IsDup)
+ UniqueIAPs.push_back(&LHS);
}
if (UniqueIAPs.empty()) continue;
- CasesO.indent(2) << "case " << I->first << ":\n";
+ CasesO.indent(2) << "case " << Entry.first << ":\n";
for (std::vector<IAPrinter*>::iterator
II = UniqueIAPs.begin(), IE = UniqueIAPs.end(); II != IE; ++II) {
@@ -1005,8 +998,9 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) {
if (!MCOpPredicates.empty())
O << "static bool " << Target.getName() << ClassName
- << "ValidateMCOperand(\n"
- << " const MCOperand &MCOp, unsigned PredicateIndex);\n";
+ << "ValidateMCOperand(const MCOperand &MCOp,\n"
+ << " const MCSubtargetInfo &STI,\n"
+ << " unsigned PredicateIndex);\n";
O << HeaderO.str();
O.indent(2) << "const char *AsmString;\n";
@@ -1078,8 +1072,9 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) {
if (!MCOpPredicates.empty()) {
O << "static bool " << Target.getName() << ClassName
- << "ValidateMCOperand(\n"
- << " const MCOperand &MCOp, unsigned PredicateIndex) {\n"
+ << "ValidateMCOperand(const MCOperand &MCOp,\n"
+ << " const MCSubtargetInfo &STI,\n"
+ << " unsigned PredicateIndex) {\n"
<< " switch (PredicateIndex) {\n"
<< " default:\n"
<< " llvm_unreachable(\"Unknown MCOperandPredicate kind\");\n"
diff --git a/utils/TableGen/Attributes.cpp b/utils/TableGen/Attributes.cpp
new file mode 100644
index 0000000000000..7b001bf14de58
--- /dev/null
+++ b/utils/TableGen/Attributes.cpp
@@ -0,0 +1,156 @@
+//===- Attributes.cpp - Generate attributes -------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/TableGen/Error.h"
+#include "llvm/TableGen/Record.h"
+#include <algorithm>
+#include <string>
+#include <vector>
+using namespace llvm;
+
+#define DEBUG_TYPE "attr-enum"
+
+namespace {
+
+class Attributes {
+public:
+ Attributes(RecordKeeper &R) : Records(R) {}
+ void emit(raw_ostream &OS);
+
+private:
+ void emitTargetIndependentEnums(raw_ostream &OS);
+ void emitFnAttrCompatCheck(raw_ostream &OS, bool IsStringAttr);
+
+ void printEnumAttrClasses(raw_ostream &OS,
+ const std::vector<Record *> &Records);
+ void printStrBoolAttrClasses(raw_ostream &OS,
+ const std::vector<Record *> &Records);
+
+ RecordKeeper &Records;
+};
+
+} // End anonymous namespace.
+
+void Attributes::emitTargetIndependentEnums(raw_ostream &OS) {
+ OS << "#ifdef GET_ATTR_ENUM\n";
+ OS << "#undef GET_ATTR_ENUM\n";
+
+ std::vector<Record*> Attrs =
+ Records.getAllDerivedDefinitions("EnumAttr");
+
+ for (auto A : Attrs)
+ OS << A->getName() << ",\n";
+
+ OS << "#endif\n";
+}
+
+void Attributes::emitFnAttrCompatCheck(raw_ostream &OS, bool IsStringAttr) {
+ OS << "#ifdef GET_ATTR_COMPAT_FUNC\n";
+ OS << "#undef GET_ATTR_COMPAT_FUNC\n";
+
+ OS << "struct EnumAttr {\n";
+ OS << " static bool isSet(const Function &Fn,\n";
+ OS << " Attribute::AttrKind Kind) {\n";
+ OS << " return Fn.hasFnAttribute(Kind);\n";
+ OS << " }\n\n";
+ OS << " static void set(Function &Fn,\n";
+ OS << " Attribute::AttrKind Kind, bool Val) {\n";
+ OS << " if (Val)\n";
+ OS << " Fn.addFnAttr(Kind);\n";
+ OS << " else\n";
+ OS << " Fn.removeFnAttr(Kind);\n";
+ OS << " }\n";
+ OS << "};\n\n";
+
+ OS << "struct StrBoolAttr {\n";
+ OS << " static bool isSet(const Function &Fn,\n";
+ OS << " StringRef Kind) {\n";
+ OS << " auto A = Fn.getFnAttribute(Kind);\n";
+ OS << " return A.getValueAsString().equals(\"true\");\n";
+ OS << " }\n\n";
+ OS << " static void set(Function &Fn,\n";
+ OS << " StringRef Kind, bool Val) {\n";
+ OS << " Fn.addFnAttr(Kind, Val ? \"true\" : \"false\");\n";
+ OS << " }\n";
+ OS << "};\n\n";
+
+ printEnumAttrClasses(OS ,Records.getAllDerivedDefinitions("EnumAttr"));
+ printStrBoolAttrClasses(OS , Records.getAllDerivedDefinitions("StrBoolAttr"));
+
+ OS << "static inline bool hasCompatibleFnAttrs(const Function &Caller,\n"
+ << " const Function &Callee) {\n";
+ OS << " bool Ret = true;\n\n";
+
+ std::vector<Record *> CompatRules =
+ Records.getAllDerivedDefinitions("CompatRule");
+
+ for (auto *Rule : CompatRules) {
+ std::string FuncName = Rule->getValueAsString("CompatFunc");
+ OS << " Ret &= " << FuncName << "(Caller, Callee);\n";
+ }
+
+ OS << "\n";
+ OS << " return Ret;\n";
+ OS << "}\n\n";
+
+ std::vector<Record *> MergeRules =
+ Records.getAllDerivedDefinitions("MergeRule");
+ OS << "static inline void mergeFnAttrs(Function &Caller,\n"
+ << " const Function &Callee) {\n";
+
+ for (auto *Rule : MergeRules) {
+ std::string FuncName = Rule->getValueAsString("MergeFunc");
+ OS << " " << FuncName << "(Caller, Callee);\n";
+ }
+
+ OS << "}\n\n";
+
+ OS << "#endif\n";
+}
+
+void Attributes::printEnumAttrClasses(raw_ostream &OS,
+ const std::vector<Record *> &Records) {
+ OS << "// EnumAttr classes\n";
+ for (const auto *R : Records) {
+ OS << "struct " << R->getName() << "Attr : EnumAttr {\n";
+ OS << " static enum Attribute::AttrKind getKind() {\n";
+ OS << " return llvm::Attribute::" << R->getName() << ";\n";
+ OS << " }\n";
+ OS << "};\n";
+ }
+ OS << "\n";
+}
+
+void Attributes::printStrBoolAttrClasses(raw_ostream &OS,
+ const std::vector<Record *> &Records) {
+ OS << "// StrBoolAttr classes\n";
+ for (const auto *R : Records) {
+ OS << "struct " << R->getName() << "Attr : StrBoolAttr {\n";
+ OS << " static const char *getKind() {\n";
+ OS << " return \"" << R->getValueAsString("AttrString") << "\";\n";
+ OS << " }\n";
+ OS << "};\n";
+ }
+ OS << "\n";
+}
+
+void Attributes::emit(raw_ostream &OS) {
+ emitTargetIndependentEnums(OS);
+ emitFnAttrCompatCheck(OS, false);
+}
+
+namespace llvm {
+
+void EmitAttributes(RecordKeeper &RK, raw_ostream &OS) {
+ Attributes(RK).emit(OS);
+}
+
+} // End llvm namespace.
diff --git a/utils/TableGen/CMakeLists.txt b/utils/TableGen/CMakeLists.txt
index feaa7c7579628..eef1540424dd1 100644
--- a/utils/TableGen/CMakeLists.txt
+++ b/utils/TableGen/CMakeLists.txt
@@ -4,6 +4,7 @@ add_tablegen(llvm-tblgen LLVM
AsmMatcherEmitter.cpp
AsmWriterEmitter.cpp
AsmWriterInst.cpp
+ Attributes.cpp
CallingConvEmitter.cpp
CodeEmitterGen.cpp
CodeGenDAGPatterns.cpp
diff --git a/utils/TableGen/CallingConvEmitter.cpp b/utils/TableGen/CallingConvEmitter.cpp
index c7519b3e15281..a47662b28558c 100644
--- a/utils/TableGen/CallingConvEmitter.cpp
+++ b/utils/TableGen/CallingConvEmitter.cpp
@@ -181,15 +181,15 @@ void CallingConvEmitter::EmitAction(Record *Action,
O << Size << ", ";
else
O << "\n" << IndentStr
- << " State.getMachineFunction().getTarget().getDataLayout()"
- "->getTypeAllocSize(EVT(LocVT).getTypeForEVT(State.getContext())),"
+ << " State.getMachineFunction().getDataLayout()."
+ "getTypeAllocSize(EVT(LocVT).getTypeForEVT(State.getContext())),"
" ";
if (Align)
O << Align;
else
O << "\n" << IndentStr
- << " State.getMachineFunction().getTarget().getDataLayout()"
- "->getABITypeAlignment(EVT(LocVT).getTypeForEVT(State.getContext()"
+ << " State.getMachineFunction().getDataLayout()."
+ "getABITypeAlignment(EVT(LocVT).getTypeForEVT(State.getContext()"
"))";
O << ");\n" << IndentStr
<< "State.addLoc(CCValAssign::getMem(ValNo, ValVT, Offset"
diff --git a/utils/TableGen/CodeGenDAGPatterns.cpp b/utils/TableGen/CodeGenDAGPatterns.cpp
index ae1cc0cfb5fd7..3ebe51e05121d 100644
--- a/utils/TableGen/CodeGenDAGPatterns.cpp
+++ b/utils/TableGen/CodeGenDAGPatterns.cpp
@@ -14,6 +14,7 @@
#include "CodeGenDAGPatterns.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/Debug.h"
@@ -84,9 +85,9 @@ bool EEVT::TypeSet::FillWithPossibleTypes(TreePattern &TP,
if (TP.hasError())
return false;
- for (unsigned i = 0, e = LegalTypes.size(); i != e; ++i)
- if (!Pred || Pred(LegalTypes[i]))
- TypeVec.push_back(LegalTypes[i]);
+ for (MVT::SimpleValueType VT : LegalTypes)
+ if (!Pred || Pred(VT))
+ TypeVec.push_back(VT);
// If we have nothing that matches the predicate, bail out.
if (TypeVec.empty()) {
@@ -107,36 +108,24 @@ bool EEVT::TypeSet::FillWithPossibleTypes(TreePattern &TP,
/// hasIntegerTypes - Return true if this TypeSet contains iAny or an
/// integer value type.
bool EEVT::TypeSet::hasIntegerTypes() const {
- for (unsigned i = 0, e = TypeVec.size(); i != e; ++i)
- if (isInteger(TypeVec[i]))
- return true;
- return false;
+ return std::any_of(TypeVec.begin(), TypeVec.end(), isInteger);
}
/// hasFloatingPointTypes - Return true if this TypeSet contains an fAny or
/// a floating point value type.
bool EEVT::TypeSet::hasFloatingPointTypes() const {
- for (unsigned i = 0, e = TypeVec.size(); i != e; ++i)
- if (isFloatingPoint(TypeVec[i]))
- return true;
- return false;
+ return std::any_of(TypeVec.begin(), TypeVec.end(), isFloatingPoint);
}
/// hasScalarTypes - Return true if this TypeSet contains a scalar value type.
bool EEVT::TypeSet::hasScalarTypes() const {
- for (unsigned i = 0, e = TypeVec.size(); i != e; ++i)
- if (isScalar(TypeVec[i]))
- return true;
- return false;
+ return std::any_of(TypeVec.begin(), TypeVec.end(), isScalar);
}
/// hasVectorTypes - Return true if this TypeSet contains a vAny or a vector
/// value type.
bool EEVT::TypeSet::hasVectorTypes() const {
- for (unsigned i = 0, e = TypeVec.size(); i != e; ++i)
- if (isVector(TypeVec[i]))
- return true;
- return false;
+ return std::any_of(TypeVec.begin(), TypeVec.end(), isVector);
}
@@ -171,7 +160,7 @@ bool EEVT::TypeSet::MergeInTypeInfo(const EEVT::TypeSet &InVT, TreePattern &TP){
return true;
}
- assert(TypeVec.size() >= 1 && InVT.TypeVec.size() >= 1 && "No unknowns");
+ assert(!TypeVec.empty() && !InVT.TypeVec.empty() && "No unknowns");
// Handle the abstract cases, seeing if we can resolve them better.
switch (TypeVec[0]) {
@@ -206,8 +195,7 @@ bool EEVT::TypeSet::MergeInTypeInfo(const EEVT::TypeSet &InVT, TreePattern &TP){
// multiple different integer types, replace them with a single iPTR.
if ((InVT.TypeVec[0] == MVT::iPTR || InVT.TypeVec[0] == MVT::iPTRAny) &&
TypeVec.size() != 1) {
- TypeVec.resize(1);
- TypeVec[0] = InVT.TypeVec[0];
+ TypeVec.assign(1, InVT.TypeVec[0]);
MadeChange = true;
}
@@ -216,25 +204,20 @@ bool EEVT::TypeSet::MergeInTypeInfo(const EEVT::TypeSet &InVT, TreePattern &TP){
// If this is a type list and the RHS is a typelist as well, eliminate entries
// from this list that aren't in the other one.
- bool MadeChange = false;
TypeSet InputSet(*this);
- for (unsigned i = 0; i != TypeVec.size(); ++i) {
- bool InInVT = false;
- for (unsigned j = 0, e = InVT.TypeVec.size(); j != e; ++j)
- if (TypeVec[i] == InVT.TypeVec[j]) {
- InInVT = true;
- break;
- }
+ TypeVec.clear();
+ std::set_intersection(InputSet.TypeVec.begin(), InputSet.TypeVec.end(),
+ InVT.TypeVec.begin(), InVT.TypeVec.end(),
+ std::back_inserter(TypeVec));
- if (InInVT) continue;
- TypeVec.erase(TypeVec.begin()+i--);
- MadeChange = true;
- }
+ // If the intersection is the same size as the original set then we're done.
+ if (TypeVec.size() == InputSet.TypeVec.size())
+ return false;
// If we removed all of our types, we have a type contradiction.
if (!TypeVec.empty())
- return MadeChange;
+ return true;
// FIXME: Really want an SMLoc here!
TP.error("Type inference contradiction found, merging '" +
@@ -249,15 +232,16 @@ bool EEVT::TypeSet::EnforceInteger(TreePattern &TP) {
// If we know nothing, then get the full set.
if (TypeVec.empty())
return FillWithPossibleTypes(TP, isInteger, "integer");
+
if (!hasFloatingPointTypes())
return false;
TypeSet InputSet(*this);
// Filter out all the fp types.
- for (unsigned i = 0; i != TypeVec.size(); ++i)
- if (!isInteger(TypeVec[i]))
- TypeVec.erase(TypeVec.begin()+i--);
+ TypeVec.erase(std::remove_if(TypeVec.begin(), TypeVec.end(),
+ std::not1(std::ptr_fun(isInteger))),
+ TypeVec.end());
if (TypeVec.empty()) {
TP.error("Type inference contradiction found, '" +
@@ -280,10 +264,10 @@ bool EEVT::TypeSet::EnforceFloatingPoint(TreePattern &TP) {
TypeSet InputSet(*this);
- // Filter out all the fp types.
- for (unsigned i = 0; i != TypeVec.size(); ++i)
- if (!isFloatingPoint(TypeVec[i]))
- TypeVec.erase(TypeVec.begin()+i--);
+ // Filter out all the integer types.
+ TypeVec.erase(std::remove_if(TypeVec.begin(), TypeVec.end(),
+ std::not1(std::ptr_fun(isFloatingPoint))),
+ TypeVec.end());
if (TypeVec.empty()) {
TP.error("Type inference contradiction found, '" +
@@ -308,9 +292,9 @@ bool EEVT::TypeSet::EnforceScalar(TreePattern &TP) {
TypeSet InputSet(*this);
// Filter out all the vector types.
- for (unsigned i = 0; i != TypeVec.size(); ++i)
- if (!isScalar(TypeVec[i]))
- TypeVec.erase(TypeVec.begin()+i--);
+ TypeVec.erase(std::remove_if(TypeVec.begin(), TypeVec.end(),
+ std::not1(std::ptr_fun(isScalar))),
+ TypeVec.end());
if (TypeVec.empty()) {
TP.error("Type inference contradiction found, '" +
@@ -333,11 +317,9 @@ bool EEVT::TypeSet::EnforceVector(TreePattern &TP) {
bool MadeChange = false;
// Filter out all the scalar types.
- for (unsigned i = 0; i != TypeVec.size(); ++i)
- if (!isVector(TypeVec[i])) {
- TypeVec.erase(TypeVec.begin()+i--);
- MadeChange = true;
- }
+ TypeVec.erase(std::remove_if(TypeVec.begin(), TypeVec.end(),
+ std::not1(std::ptr_fun(isVector))),
+ TypeVec.end());
if (TypeVec.empty()) {
TP.error("Type inference contradiction found, '" +
@@ -350,7 +332,7 @@ bool EEVT::TypeSet::EnforceVector(TreePattern &TP) {
/// EnforceSmallerThan - 'this' must be a smaller VT than Other. For vectors
-/// this shoud be based on the element type. Update this and other based on
+/// this should be based on the element type. Update this and other based on
/// this information.
bool EEVT::TypeSet::EnforceSmallerThan(EEVT::TypeSet &Other, TreePattern &TP) {
if (TP.hasError())
@@ -404,26 +386,33 @@ bool EEVT::TypeSet::EnforceSmallerThan(EEVT::TypeSet &Other, TreePattern &TP) {
// type size is smaller than the scalar size of the smallest type. For
// vectors, we also need to make sure that the total size is no larger than
// the size of the smallest type.
- TypeSet InputSet(Other);
- MVT Smallest = TypeVec[0];
- for (unsigned i = 0; i != Other.TypeVec.size(); ++i) {
- MVT OtherVT = Other.TypeVec[i];
- // Don't compare vector and non-vector types.
- if (OtherVT.isVector() != Smallest.isVector())
- continue;
- // The getSizeInBits() check here is only needed for vectors, but is
- // a subset of the scalar check for scalars so no need to qualify.
- if (OtherVT.getScalarSizeInBits() <= Smallest.getScalarSizeInBits() ||
- OtherVT.getSizeInBits() < Smallest.getSizeInBits()) {
- Other.TypeVec.erase(Other.TypeVec.begin()+i--);
- MadeChange = true;
- }
- }
+ {
+ TypeSet InputSet(Other);
+ MVT Smallest = *std::min_element(TypeVec.begin(), TypeVec.end(),
+ [](MVT A, MVT B) {
+ return A.getScalarSizeInBits() < B.getScalarSizeInBits() ||
+ (A.getScalarSizeInBits() == B.getScalarSizeInBits() &&
+ A.getSizeInBits() < B.getSizeInBits());
+ });
- if (Other.TypeVec.empty()) {
- TP.error("Type inference contradiction found, '" + InputSet.getName() +
- "' has nothing larger than '" + getName() +"'!");
- return false;
+ auto I = std::remove_if(Other.TypeVec.begin(), Other.TypeVec.end(),
+ [Smallest](MVT OtherVT) {
+ // Don't compare vector and non-vector types.
+ if (OtherVT.isVector() != Smallest.isVector())
+ return false;
+ // The getSizeInBits() check here is only needed for vectors, but is
+ // a subset of the scalar check for scalars so no need to qualify.
+ return OtherVT.getScalarSizeInBits() <= Smallest.getScalarSizeInBits()||
+ OtherVT.getSizeInBits() < Smallest.getSizeInBits();
+ });
+ MadeChange |= I != Other.TypeVec.end(); // If we're about to remove types.
+ Other.TypeVec.erase(I, Other.TypeVec.end());
+
+ if (Other.TypeVec.empty()) {
+ TP.error("Type inference contradiction found, '" + InputSet.getName() +
+ "' has nothing larger than '" + getName() +"'!");
+ return false;
+ }
}
// Okay, find the largest type from the other set and remove anything the
@@ -431,32 +420,36 @@ bool EEVT::TypeSet::EnforceSmallerThan(EEVT::TypeSet &Other, TreePattern &TP) {
// type size is larger than the scalar size of the largest type. For
// vectors, we also need to make sure that the total size is no smaller than
// the size of the largest type.
- InputSet = TypeSet(*this);
- MVT Largest = Other.TypeVec[Other.TypeVec.size()-1];
- for (unsigned i = 0; i != TypeVec.size(); ++i) {
- MVT OtherVT = TypeVec[i];
- // Don't compare vector and non-vector types.
- if (OtherVT.isVector() != Largest.isVector())
- continue;
- // The getSizeInBits() check here is only needed for vectors, but is
- // a subset of the scalar check for scalars so no need to qualify.
- if (OtherVT.getScalarSizeInBits() >= Largest.getScalarSizeInBits() ||
- OtherVT.getSizeInBits() > Largest.getSizeInBits()) {
- TypeVec.erase(TypeVec.begin()+i--);
- MadeChange = true;
- }
- }
+ {
+ TypeSet InputSet(*this);
+ MVT Largest = *std::max_element(Other.TypeVec.begin(), Other.TypeVec.end(),
+ [](MVT A, MVT B) {
+ return A.getScalarSizeInBits() < B.getScalarSizeInBits() ||
+ (A.getScalarSizeInBits() == B.getScalarSizeInBits() &&
+ A.getSizeInBits() < B.getSizeInBits());
+ });
+ auto I = std::remove_if(TypeVec.begin(), TypeVec.end(),
+ [Largest](MVT OtherVT) {
+ // Don't compare vector and non-vector types.
+ if (OtherVT.isVector() != Largest.isVector())
+ return false;
+ return OtherVT.getScalarSizeInBits() >= Largest.getScalarSizeInBits() ||
+ OtherVT.getSizeInBits() > Largest.getSizeInBits();
+ });
+ MadeChange |= I != TypeVec.end(); // If we're about to remove types.
+ TypeVec.erase(I, TypeVec.end());
- if (TypeVec.empty()) {
- TP.error("Type inference contradiction found, '" + InputSet.getName() +
- "' has nothing smaller than '" + Other.getName() +"'!");
- return false;
+ if (TypeVec.empty()) {
+ TP.error("Type inference contradiction found, '" + InputSet.getName() +
+ "' has nothing smaller than '" + Other.getName() +"'!");
+ return false;
+ }
}
return MadeChange;
}
-/// EnforceVectorEltTypeIs - 'this' is now constrainted to be a vector type
+/// EnforceVectorEltTypeIs - 'this' is now constrained to be a vector type
/// whose element is specified by VTOperand.
bool EEVT::TypeSet::EnforceVectorEltTypeIs(MVT::SimpleValueType VT,
TreePattern &TP) {
@@ -467,24 +460,24 @@ bool EEVT::TypeSet::EnforceVectorEltTypeIs(MVT::SimpleValueType VT,
TypeSet InputSet(*this);
// Filter out all the types which don't have the right element type.
- for (unsigned i = 0; i != TypeVec.size(); ++i) {
- assert(isVector(TypeVec[i]) && "EnforceVector didn't work");
- if (MVT(TypeVec[i]).getVectorElementType().SimpleTy != VT) {
- TypeVec.erase(TypeVec.begin()+i--);
- MadeChange = true;
- }
- }
+ auto I = std::remove_if(TypeVec.begin(), TypeVec.end(),
+ [VT](MVT VVT) {
+ return VVT.getVectorElementType().SimpleTy != VT;
+ });
+ MadeChange |= I != TypeVec.end();
+ TypeVec.erase(I, TypeVec.end());
if (TypeVec.empty()) { // FIXME: Really want an SMLoc here!
TP.error("Type inference contradiction found, forcing '" +
- InputSet.getName() + "' to have a vector element");
+ InputSet.getName() + "' to have a vector element of type " +
+ getEnumName(VT));
return false;
}
return MadeChange;
}
-/// EnforceVectorEltTypeIs - 'this' is now constrainted to be a vector type
+/// EnforceVectorEltTypeIs - 'this' is now constrained to be a vector type
/// whose element is specified by VTOperand.
bool EEVT::TypeSet::EnforceVectorEltTypeIs(EEVT::TypeSet &VTOperand,
TreePattern &TP) {
@@ -500,8 +493,7 @@ bool EEVT::TypeSet::EnforceVectorEltTypeIs(EEVT::TypeSet &VTOperand,
if (isConcrete()) {
MVT IVT = getConcrete();
IVT = IVT.getVectorElementType();
- return MadeChange |
- VTOperand.MergeInTypeInfo(IVT.SimpleTy, TP);
+ return MadeChange || VTOperand.MergeInTypeInfo(IVT.SimpleTy, TP);
}
// If the scalar type is known, filter out vector types whose element types
@@ -511,26 +503,12 @@ bool EEVT::TypeSet::EnforceVectorEltTypeIs(EEVT::TypeSet &VTOperand,
MVT::SimpleValueType VT = VTOperand.getConcrete();
- TypeSet InputSet(*this);
-
- // Filter out all the types which don't have the right element type.
- for (unsigned i = 0; i != TypeVec.size(); ++i) {
- assert(isVector(TypeVec[i]) && "EnforceVector didn't work");
- if (MVT(TypeVec[i]).getVectorElementType().SimpleTy != VT) {
- TypeVec.erase(TypeVec.begin()+i--);
- MadeChange = true;
- }
- }
+ MadeChange |= EnforceVectorEltTypeIs(VT, TP);
- if (TypeVec.empty()) { // FIXME: Really want an SMLoc here!
- TP.error("Type inference contradiction found, forcing '" +
- InputSet.getName() + "' to have a vector element");
- return false;
- }
return MadeChange;
}
-/// EnforceVectorSubVectorTypeIs - 'this' is now constrainted to be a
+/// EnforceVectorSubVectorTypeIs - 'this' is now constrained to be a
/// vector type specified by VTOperand.
bool EEVT::TypeSet::EnforceVectorSubVectorTypeIs(EEVT::TypeSet &VTOperand,
TreePattern &TP) {
@@ -569,13 +547,13 @@ bool EEVT::TypeSet::EnforceVectorSubVectorTypeIs(EEVT::TypeSet &VTOperand,
// Only keep types that have less elements than VTOperand.
TypeSet InputSet(VTOperand);
- for (unsigned i = 0; i != VTOperand.TypeVec.size(); ++i) {
- assert(isVector(VTOperand.TypeVec[i]) && "EnforceVector didn't work");
- if (MVT(VTOperand.TypeVec[i]).getVectorNumElements() >= NumElems) {
- VTOperand.TypeVec.erase(VTOperand.TypeVec.begin()+i--);
- MadeChange = true;
- }
- }
+ auto I = std::remove_if(VTOperand.TypeVec.begin(), VTOperand.TypeVec.end(),
+ [NumElems](MVT VVT) {
+ return VVT.getVectorNumElements() >= NumElems;
+ });
+ MadeChange |= I != VTOperand.TypeVec.end();
+ VTOperand.TypeVec.erase(I, VTOperand.TypeVec.end());
+
if (VTOperand.TypeVec.empty()) { // FIXME: Really want an SMLoc here!
TP.error("Type inference contradiction found, forcing '" +
InputSet.getName() + "' to have less vector elements than '" +
@@ -593,13 +571,13 @@ bool EEVT::TypeSet::EnforceVectorSubVectorTypeIs(EEVT::TypeSet &VTOperand,
// Only keep types that have more elements than 'this'.
TypeSet InputSet(*this);
- for (unsigned i = 0; i != TypeVec.size(); ++i) {
- assert(isVector(TypeVec[i]) && "EnforceVector didn't work");
- if (MVT(TypeVec[i]).getVectorNumElements() <= NumElems) {
- TypeVec.erase(TypeVec.begin()+i--);
- MadeChange = true;
- }
- }
+ auto I = std::remove_if(TypeVec.begin(), TypeVec.end(),
+ [NumElems](MVT VVT) {
+ return VVT.getVectorNumElements() <= NumElems;
+ });
+ MadeChange |= I != TypeVec.end();
+ TypeVec.erase(I, TypeVec.end());
+
if (TypeVec.empty()) { // FIXME: Really want an SMLoc here!
TP.error("Type inference contradiction found, forcing '" +
InputSet.getName() + "' to have more vector elements than '" +
@@ -611,7 +589,7 @@ bool EEVT::TypeSet::EnforceVectorSubVectorTypeIs(EEVT::TypeSet &VTOperand,
return MadeChange;
}
-/// EnforceVectorSameNumElts - 'this' is now constrainted to
+/// EnforceVectorSameNumElts - 'this' is now constrained to
/// be a vector with same num elements as VTOperand.
bool EEVT::TypeSet::EnforceVectorSameNumElts(EEVT::TypeSet &VTOperand,
TreePattern &TP) {
@@ -628,16 +606,16 @@ bool EEVT::TypeSet::EnforceVectorSameNumElts(EEVT::TypeSet &VTOperand,
MVT IVT = getConcrete();
unsigned NumElems = IVT.getVectorNumElements();
- // Only keep types that have same elements as VTOperand.
+ // Only keep types that have same elements as 'this'.
TypeSet InputSet(VTOperand);
- for (unsigned i = 0; i != VTOperand.TypeVec.size(); ++i) {
- assert(isVector(VTOperand.TypeVec[i]) && "EnforceVector didn't work");
- if (MVT(VTOperand.TypeVec[i]).getVectorNumElements() != NumElems) {
- VTOperand.TypeVec.erase(VTOperand.TypeVec.begin()+i--);
- MadeChange = true;
- }
- }
+ auto I = std::remove_if(VTOperand.TypeVec.begin(), VTOperand.TypeVec.end(),
+ [NumElems](MVT VVT) {
+ return VVT.getVectorNumElements() != NumElems;
+ });
+ MadeChange |= I != VTOperand.TypeVec.end();
+ VTOperand.TypeVec.erase(I, VTOperand.TypeVec.end());
+
if (VTOperand.TypeVec.empty()) { // FIXME: Really want an SMLoc here!
TP.error("Type inference contradiction found, forcing '" +
InputSet.getName() + "' to have same number elements as '" +
@@ -648,16 +626,16 @@ bool EEVT::TypeSet::EnforceVectorSameNumElts(EEVT::TypeSet &VTOperand,
MVT IVT = VTOperand.getConcrete();
unsigned NumElems = IVT.getVectorNumElements();
- // Only keep types that have same elements as 'this'.
+ // Only keep types that have same elements as VTOperand.
TypeSet InputSet(*this);
- for (unsigned i = 0; i != TypeVec.size(); ++i) {
- assert(isVector(TypeVec[i]) && "EnforceVector didn't work");
- if (MVT(TypeVec[i]).getVectorNumElements() != NumElems) {
- TypeVec.erase(TypeVec.begin()+i--);
- MadeChange = true;
- }
- }
+ auto I = std::remove_if(TypeVec.begin(), TypeVec.end(),
+ [NumElems](MVT VVT) {
+ return VVT.getVectorNumElements() != NumElems;
+ });
+ MadeChange |= I != TypeVec.end();
+ TypeVec.erase(I, TypeVec.end());
+
if (TypeVec.empty()) { // FIXME: Really want an SMLoc here!
TP.error("Type inference contradiction found, forcing '" +
InputSet.getName() + "' to have same number elements than '" +
@@ -669,15 +647,66 @@ bool EEVT::TypeSet::EnforceVectorSameNumElts(EEVT::TypeSet &VTOperand,
return MadeChange;
}
+/// EnforceSameSize - 'this' is now constrained to be same size as VTOperand.
+bool EEVT::TypeSet::EnforceSameSize(EEVT::TypeSet &VTOperand,
+ TreePattern &TP) {
+ if (TP.hasError())
+ return false;
+
+ bool MadeChange = false;
+
+ // If we know one of the types, it forces the other type agree.
+ if (isConcrete()) {
+ MVT IVT = getConcrete();
+ unsigned Size = IVT.getSizeInBits();
+
+ // Only keep types that have the same size as 'this'.
+ TypeSet InputSet(VTOperand);
+
+ auto I = std::remove_if(VTOperand.TypeVec.begin(), VTOperand.TypeVec.end(),
+ [&](MVT VT) {
+ return VT.getSizeInBits() != Size;
+ });
+ MadeChange |= I != VTOperand.TypeVec.end();
+ VTOperand.TypeVec.erase(I, VTOperand.TypeVec.end());
+
+ if (VTOperand.TypeVec.empty()) { // FIXME: Really want an SMLoc here!
+ TP.error("Type inference contradiction found, forcing '" +
+ InputSet.getName() + "' to have same size as '" +
+ getName() + "'");
+ return false;
+ }
+ } else if (VTOperand.isConcrete()) {
+ MVT IVT = VTOperand.getConcrete();
+ unsigned Size = IVT.getSizeInBits();
+
+ // Only keep types that have the same size as VTOperand.
+ TypeSet InputSet(*this);
+
+ auto I = std::remove_if(TypeVec.begin(), TypeVec.end(),
+ [&](MVT VT) {
+ return VT.getSizeInBits() != Size;
+ });
+ MadeChange |= I != TypeVec.end();
+ TypeVec.erase(I, TypeVec.end());
+
+ if (TypeVec.empty()) { // FIXME: Really want an SMLoc here!
+ TP.error("Type inference contradiction found, forcing '" +
+ InputSet.getName() + "' to have same size as '" +
+ VTOperand.getName() + "'");
+ return false;
+ }
+ }
+
+ return MadeChange;
+}
+
//===----------------------------------------------------------------------===//
// Helpers for working with extended types.
/// Dependent variable map for CodeGenDAGPattern variant generation
typedef std::map<std::string, int> DepVarMap;
-/// Const iterator shorthand for DepVarMap
-typedef DepVarMap::const_iterator DepVarMap_citer;
-
static void FindDepVarsOf(TreePatternNode *N, DepVarMap &DepMap) {
if (N->isLeaf()) {
if (isa<DefInit>(N->getLeafValue()))
@@ -692,9 +721,9 @@ static void FindDepVarsOf(TreePatternNode *N, DepVarMap &DepMap) {
static void FindDepVars(TreePatternNode *N, MultipleUseVarSet &DepVars) {
DepVarMap depcounts;
FindDepVarsOf(N, depcounts);
- for (DepVarMap_citer i = depcounts.begin(); i != depcounts.end(); ++i) {
- if (i->second > 1) // std::pair<std::string, int>
- DepVars.insert(i->first);
+ for (const std::pair<std::string, int> &Pair : depcounts) {
+ if (Pair.second > 1)
+ DepVars.insert(Pair.first);
}
}
@@ -705,9 +734,8 @@ static void DumpDepVars(MultipleUseVarSet &DepVars) {
DEBUG(errs() << "<empty set>");
} else {
DEBUG(errs() << "[ ");
- for (MultipleUseVarSet::const_iterator i = DepVars.begin(),
- e = DepVars.end(); i != e; ++i) {
- DEBUG(errs() << (*i) << " ");
+ for (const std::string &DepVar : DepVars) {
+ DEBUG(errs() << DepVar << " ");
}
DEBUG(errs() << "]");
}
@@ -771,7 +799,7 @@ std::string TreePredicateFn::getCodeToRunOnSDNode() const {
if (ClassName == "SDNode")
Result = " SDNode *N = Node;\n";
else
- Result = " " + ClassName + "*N = cast<" + ClassName + ">(Node);\n";
+ Result = " auto *N = cast<" + ClassName + ">(Node);\n";
return Result + getPredCode();
}
@@ -841,7 +869,7 @@ getPatternComplexity(const CodeGenDAGPatterns &CGP) const {
/// pattern's predicates concatenated with "&&" operators.
///
std::string PatternToMatch::getPredicateCheck() const {
- std::string PredicateCheck;
+ SmallVector<Record *, 4> PredicateRecs;
for (Init *I : Predicates->getValues()) {
if (DefInit *Pred = dyn_cast<DefInit>(I)) {
Record *Def = Pred->getDef();
@@ -851,13 +879,20 @@ std::string PatternToMatch::getPredicateCheck() const {
#endif
llvm_unreachable("Unknown predicate type!");
}
- if (!PredicateCheck.empty())
- PredicateCheck += " && ";
- PredicateCheck += "(" + Def->getValueAsString("CondString") + ")";
+ PredicateRecs.push_back(Def);
}
}
+ // Sort so that different orders get canonicalized to the same string.
+ std::sort(PredicateRecs.begin(), PredicateRecs.end(), LessRecord());
+
+ SmallString<128> PredicateCheck;
+ for (Record *Pred : PredicateRecs) {
+ if (!PredicateCheck.empty())
+ PredicateCheck += " && ";
+ PredicateCheck += "(" + Pred->getValueAsString("CondString") + ")";
+ }
- return PredicateCheck;
+ return PredicateCheck.str();
}
//===----------------------------------------------------------------------===//
@@ -912,6 +947,10 @@ SDTypeConstraint::SDTypeConstraint(Record *R) {
ConstraintType = SDTCisSameNumEltsAs;
x.SDTCisSameNumEltsAs_Info.OtherOperandNum =
R->getValueAsInt("OtherOperandNum");
+ } else if (R->isSubClassOf("SDTCisSameSizeAs")) {
+ ConstraintType = SDTCisSameSizeAs;
+ x.SDTCisSameSizeAs_Info.OtherOperandNum =
+ R->getValueAsInt("OtherOperandNum");
} else {
PrintFatalError("Unrecognized SDTypeConstraint '" + R->getName() + "'!\n");
}
@@ -1041,6 +1080,14 @@ bool SDTypeConstraint::ApplyTypeConstraint(TreePatternNode *N,
return OtherNode->getExtType(OResNo).
EnforceVectorSameNumElts(NodeToApply->getExtType(ResNo), TP);
}
+ case SDTCisSameSizeAs: {
+ unsigned OResNo = 0;
+ TreePatternNode *OtherNode =
+ getOperandNum(x.SDTCisSameSizeAs_Info.OtherOperandNum,
+ N, NodeInfo, OResNo);
+ return OtherNode->getExtType(OResNo).
+ EnforceSameSize(NodeToApply->getExtType(ResNo), TP);
+ }
}
llvm_unreachable("Invalid ConstraintType!");
}
@@ -1091,33 +1138,32 @@ SDNodeInfo::SDNodeInfo(Record *R) : Def(R) {
// Parse the properties.
Properties = 0;
- std::vector<Record*> PropList = R->getValueAsListOfDefs("Properties");
- for (unsigned i = 0, e = PropList.size(); i != e; ++i) {
- if (PropList[i]->getName() == "SDNPCommutative") {
+ for (Record *Property : R->getValueAsListOfDefs("Properties")) {
+ if (Property->getName() == "SDNPCommutative") {
Properties |= 1 << SDNPCommutative;
- } else if (PropList[i]->getName() == "SDNPAssociative") {
+ } else if (Property->getName() == "SDNPAssociative") {
Properties |= 1 << SDNPAssociative;
- } else if (PropList[i]->getName() == "SDNPHasChain") {
+ } else if (Property->getName() == "SDNPHasChain") {
Properties |= 1 << SDNPHasChain;
- } else if (PropList[i]->getName() == "SDNPOutGlue") {
+ } else if (Property->getName() == "SDNPOutGlue") {
Properties |= 1 << SDNPOutGlue;
- } else if (PropList[i]->getName() == "SDNPInGlue") {
+ } else if (Property->getName() == "SDNPInGlue") {
Properties |= 1 << SDNPInGlue;
- } else if (PropList[i]->getName() == "SDNPOptInGlue") {
+ } else if (Property->getName() == "SDNPOptInGlue") {
Properties |= 1 << SDNPOptInGlue;
- } else if (PropList[i]->getName() == "SDNPMayStore") {
+ } else if (Property->getName() == "SDNPMayStore") {
Properties |= 1 << SDNPMayStore;
- } else if (PropList[i]->getName() == "SDNPMayLoad") {
+ } else if (Property->getName() == "SDNPMayLoad") {
Properties |= 1 << SDNPMayLoad;
- } else if (PropList[i]->getName() == "SDNPSideEffect") {
+ } else if (Property->getName() == "SDNPSideEffect") {
Properties |= 1 << SDNPSideEffect;
- } else if (PropList[i]->getName() == "SDNPMemOperand") {
+ } else if (Property->getName() == "SDNPMemOperand") {
Properties |= 1 << SDNPMemOperand;
- } else if (PropList[i]->getName() == "SDNPVariadic") {
+ } else if (Property->getName() == "SDNPVariadic") {
Properties |= 1 << SDNPVariadic;
} else {
PrintFatalError("Unknown SD Node property '" +
- PropList[i]->getName() + "' on node '" +
+ Property->getName() + "' on node '" +
R->getName() + "'!");
}
}
@@ -1138,15 +1184,15 @@ MVT::SimpleValueType SDNodeInfo::getKnownType(unsigned ResNo) const {
"We only work with nodes with zero or one result so far!");
assert(ResNo == 0 && "Only handles single result nodes so far");
- for (unsigned i = 0, e = TypeConstraints.size(); i != e; ++i) {
+ for (const SDTypeConstraint &Constraint : TypeConstraints) {
// Make sure that this applies to the correct node result.
- if (TypeConstraints[i].OperandNo >= NumResults) // FIXME: need value #
+ if (Constraint.OperandNo >= NumResults) // FIXME: need value #
continue;
- switch (TypeConstraints[i].ConstraintType) {
+ switch (Constraint.ConstraintType) {
default: break;
case SDTypeConstraint::SDTCisVT:
- return TypeConstraints[i].x.SDTCisVT_Info.VT;
+ return Constraint.x.SDTCisVT_Info.VT;
case SDTypeConstraint::SDTCisPtrTy:
return MVT::iPTR;
}
@@ -1247,8 +1293,8 @@ void TreePatternNode::print(raw_ostream &OS) const {
OS << ")";
}
- for (unsigned i = 0, e = PredicateFns.size(); i != e; ++i)
- OS << "<<P:" << PredicateFns[i].getFnName() << ">>";
+ for (const TreePredicateFn &Pred : PredicateFns)
+ OS << "<<P:" << Pred.getFnName() << ">>";
if (TransformFn)
OS << "<<X:" << TransformFn->getName() << ">>";
if (!getName().empty())
@@ -1315,8 +1361,8 @@ TreePatternNode *TreePatternNode::clone() const {
/// RemoveAllTypes - Recursively strip all the types of this tree.
void TreePatternNode::RemoveAllTypes() {
- for (unsigned i = 0, e = Types.size(); i != e; ++i)
- Types[i] = EEVT::TypeSet(); // Reset to unknown type.
+ // Reset to unknown type.
+ std::fill(Types.begin(), Types.end(), EEVT::TypeSet());
if (isLeaf()) return;
for (unsigned i = 0, e = getNumChildren(); i != e; ++i)
getChild(i)->RemoveAllTypes();
@@ -1410,8 +1456,8 @@ TreePatternNode *TreePatternNode::InlinePatternFragments(TreePattern &TP) {
FragTree->UpdateNodeType(i, getExtType(i), TP);
// Transfer in the old predicates.
- for (unsigned i = 0, e = getPredicateFns().size(); i != e; ++i)
- FragTree->addPredicateFn(getPredicateFns()[i]);
+ for (const TreePredicateFn &Pred : getPredicateFns())
+ FragTree->addPredicateFn(Pred);
// Get a new copy of this fragment to stitch into here.
//delete this; // FIXME: implement refcounting!
@@ -2024,8 +2070,8 @@ void TreePattern::error(const Twine &Msg) {
}
void TreePattern::ComputeNamedNodes() {
- for (unsigned i = 0, e = Trees.size(); i != e; ++i)
- ComputeNamedNodes(Trees[i]);
+ for (TreePatternNode *Tree : Trees)
+ ComputeNamedNodes(Tree);
}
void TreePattern::ComputeNamedNodes(TreePatternNode *N) {
@@ -2251,53 +2297,52 @@ InferAllTypes(const StringMap<SmallVector<TreePatternNode*,1> > *InNamedTypes) {
bool MadeChange = true;
while (MadeChange) {
MadeChange = false;
- for (unsigned i = 0, e = Trees.size(); i != e; ++i) {
- MadeChange |= Trees[i]->ApplyTypeConstraints(*this, false);
- MadeChange |= SimplifyTree(Trees[i]);
+ for (TreePatternNode *Tree : Trees) {
+ MadeChange |= Tree->ApplyTypeConstraints(*this, false);
+ MadeChange |= SimplifyTree(Tree);
}
// If there are constraints on our named nodes, apply them.
- for (StringMap<SmallVector<TreePatternNode*,1> >::iterator
- I = NamedNodes.begin(), E = NamedNodes.end(); I != E; ++I) {
- SmallVectorImpl<TreePatternNode*> &Nodes = I->second;
+ for (auto &Entry : NamedNodes) {
+ SmallVectorImpl<TreePatternNode*> &Nodes = Entry.second;
// If we have input named node types, propagate their types to the named
// values here.
if (InNamedTypes) {
- if (!InNamedTypes->count(I->getKey())) {
- error("Node '" + std::string(I->getKey()) +
+ if (!InNamedTypes->count(Entry.getKey())) {
+ error("Node '" + std::string(Entry.getKey()) +
"' in output pattern but not input pattern");
return true;
}
const SmallVectorImpl<TreePatternNode*> &InNodes =
- InNamedTypes->find(I->getKey())->second;
+ InNamedTypes->find(Entry.getKey())->second;
// The input types should be fully resolved by now.
- for (unsigned i = 0, e = Nodes.size(); i != e; ++i) {
+ for (TreePatternNode *Node : Nodes) {
// If this node is a register class, and it is the root of the pattern
// then we're mapping something onto an input register. We allow
// changing the type of the input register in this case. This allows
// us to match things like:
// def : Pat<(v1i64 (bitconvert(v2i32 DPR:$src))), (v1i64 DPR:$src)>;
- if (Nodes[i] == Trees[0] && Nodes[i]->isLeaf()) {
- DefInit *DI = dyn_cast<DefInit>(Nodes[i]->getLeafValue());
+ if (Node == Trees[0] && Node->isLeaf()) {
+ DefInit *DI = dyn_cast<DefInit>(Node->getLeafValue());
if (DI && (DI->getDef()->isSubClassOf("RegisterClass") ||
DI->getDef()->isSubClassOf("RegisterOperand")))
continue;
}
- assert(Nodes[i]->getNumTypes() == 1 &&
+ assert(Node->getNumTypes() == 1 &&
InNodes[0]->getNumTypes() == 1 &&
"FIXME: cannot name multiple result nodes yet");
- MadeChange |= Nodes[i]->UpdateNodeType(0, InNodes[0]->getExtType(0),
- *this);
+ MadeChange |= Node->UpdateNodeType(0, InNodes[0]->getExtType(0),
+ *this);
}
}
// If there are multiple nodes with the same name, they must all have the
// same type.
- if (I->second.size() > 1) {
+ if (Entry.second.size() > 1) {
for (unsigned i = 0, e = Nodes.size()-1; i != e; ++i) {
TreePatternNode *N1 = Nodes[i], *N2 = Nodes[i+1];
assert(N1->getNumTypes() == 1 && N2->getNumTypes() == 1 &&
@@ -2311,8 +2356,8 @@ InferAllTypes(const StringMap<SmallVector<TreePatternNode*,1> > *InNamedTypes) {
}
bool HasUnresolvedTypes = false;
- for (unsigned i = 0, e = Trees.size(); i != e; ++i)
- HasUnresolvedTypes |= Trees[i]->ContainsUnresolvedType();
+ for (const TreePatternNode *Tree : Trees)
+ HasUnresolvedTypes |= Tree->ContainsUnresolvedType();
return !HasUnresolvedTypes;
}
@@ -2328,9 +2373,9 @@ void TreePattern::print(raw_ostream &OS) const {
if (Trees.size() > 1)
OS << "[\n";
- for (unsigned i = 0, e = Trees.size(); i != e; ++i) {
+ for (const TreePatternNode *Tree : Trees) {
OS << "\t";
- Trees[i]->print(OS);
+ Tree->print(OS);
OS << "\n";
}
@@ -2425,14 +2470,14 @@ void CodeGenDAGPatterns::ParsePatternFragments(bool OutFrags) {
std::vector<Record*> Fragments = Records.getAllDerivedDefinitions("PatFrag");
// First step, parse all of the fragments.
- for (unsigned i = 0, e = Fragments.size(); i != e; ++i) {
- if (OutFrags != Fragments[i]->isSubClassOf("OutPatFrag"))
+ for (Record *Frag : Fragments) {
+ if (OutFrags != Frag->isSubClassOf("OutPatFrag"))
continue;
- DagInit *Tree = Fragments[i]->getValueAsDag("Fragment");
+ DagInit *Tree = Frag->getValueAsDag("Fragment");
TreePattern *P =
- (PatternFragments[Fragments[i]] = llvm::make_unique<TreePattern>(
- Fragments[i], Tree, !Fragments[i]->isSubClassOf("OutPatFrag"),
+ (PatternFragments[Frag] = llvm::make_unique<TreePattern>(
+ Frag, Tree, !Frag->isSubClassOf("OutPatFrag"),
*this)).get();
// Validate the argument list, converting it to set, to discard duplicates.
@@ -2443,7 +2488,7 @@ void CodeGenDAGPatterns::ParsePatternFragments(bool OutFrags) {
P->error("Cannot have unnamed 'node' values in pattern fragment!");
// Parse the operands list.
- DagInit *OpsList = Fragments[i]->getValueAsDag("Operands");
+ DagInit *OpsList = Frag->getValueAsDag("Operands");
DefInit *OpsOp = dyn_cast<DefInit>(OpsList->getOperator());
// Special cases: ops == outs == ins. Different names are used to
// improve readability.
@@ -2480,18 +2525,18 @@ void CodeGenDAGPatterns::ParsePatternFragments(bool OutFrags) {
// If there is a node transformation corresponding to this, keep track of
// it.
- Record *Transform = Fragments[i]->getValueAsDef("OperandTransform");
+ Record *Transform = Frag->getValueAsDef("OperandTransform");
if (!getSDNodeTransform(Transform).second.empty()) // not noop xform?
P->getOnlyTree()->setTransformFn(Transform);
}
// Now that we've parsed all of the tree fragments, do a closure on them so
// that there are not references to PatFrags left inside of them.
- for (unsigned i = 0, e = Fragments.size(); i != e; ++i) {
- if (OutFrags != Fragments[i]->isSubClassOf("OutPatFrag"))
+ for (Record *Frag : Fragments) {
+ if (OutFrags != Frag->isSubClassOf("OutPatFrag"))
continue;
- TreePattern &ThePat = *PatternFragments[Fragments[i]];
+ TreePattern &ThePat = *PatternFragments[Frag];
ThePat.InlinePatternFragments();
// Infer as many types as possible. Don't worry about it if we don't infer
@@ -2815,7 +2860,7 @@ static bool InferFromPattern(CodeGenInstruction &InstInfo,
if (InstInfo.mayLoad != PatInfo.mayLoad && !InstInfo.mayLoad_Unset) {
// Allow explicitly setting mayLoad = 1, even when the pattern has no loads.
- // Some targets translate imediates to loads.
+ // Some targets translate immediates to loads.
if (!InstInfo.mayLoad) {
Error = true;
PrintError(PatDef->getLoc(), "Pattern doesn't match mayLoad = " +
@@ -3065,11 +3110,11 @@ const DAGInstruction &CodeGenDAGPatterns::parseInstructionPattern(
void CodeGenDAGPatterns::ParseInstructions() {
std::vector<Record*> Instrs = Records.getAllDerivedDefinitions("Instruction");
- for (unsigned i = 0, e = Instrs.size(); i != e; ++i) {
+ for (Record *Instr : Instrs) {
ListInit *LI = nullptr;
- if (isa<ListInit>(Instrs[i]->getValueInit("Pattern")))
- LI = Instrs[i]->getValueAsListInit("Pattern");
+ if (isa<ListInit>(Instr->getValueInit("Pattern")))
+ LI = Instr->getValueAsListInit("Pattern");
// If there is no pattern, only collect minimal information about the
// instruction for its operand list. We have to assume that there is one
@@ -3081,7 +3126,7 @@ void CodeGenDAGPatterns::ParseInstructions() {
std::vector<Record*> Results;
std::vector<Record*> Operands;
- CodeGenInstruction &InstInfo = Target.getInstruction(Instrs[i]);
+ CodeGenInstruction &InstInfo = Target.getInstruction(Instr);
if (InstInfo.Operands.size() != 0) {
for (unsigned j = 0, e = InstInfo.Operands.NumDefs; j < e; ++j)
@@ -3095,12 +3140,12 @@ void CodeGenDAGPatterns::ParseInstructions() {
// Create and insert the instruction.
std::vector<Record*> ImpResults;
- Instructions.insert(std::make_pair(Instrs[i],
+ Instructions.insert(std::make_pair(Instr,
DAGInstruction(nullptr, Results, Operands, ImpResults)));
continue; // no pattern.
}
- CodeGenInstruction &CGI = Target.getInstruction(Instrs[i]);
+ CodeGenInstruction &CGI = Target.getInstruction(Instr);
const DAGInstruction &DI = parseInstructionPattern(CGI, LI, Instructions);
(void)DI;
@@ -3108,10 +3153,8 @@ void CodeGenDAGPatterns::ParseInstructions() {
}
// If we can, convert the instructions to be patterns that are matched!
- for (std::map<Record*, DAGInstruction, LessRecordByID>::iterator II =
- Instructions.begin(),
- E = Instructions.end(); II != E; ++II) {
- DAGInstruction &TheInst = II->second;
+ for (auto &Entry : Instructions) {
+ DAGInstruction &TheInst = Entry.second;
TreePattern *I = TheInst.getPattern();
if (!I) continue; // No pattern.
@@ -3126,7 +3169,7 @@ void CodeGenDAGPatterns::ParseInstructions() {
SrcPattern = Pattern;
}
- Record *Instr = II->first;
+ Record *Instr = Entry.first;
AddPatternToMatch(I,
PatternToMatch(Instr,
Instr->getValueAsListInit("Predicates"),
@@ -3187,19 +3230,18 @@ void CodeGenDAGPatterns::AddPatternToMatch(TreePattern *Pattern,
// Scan all of the named values in the destination pattern, rejecting them if
// they don't exist in the input pattern.
- for (std::map<std::string, NameRecord>::iterator
- I = DstNames.begin(), E = DstNames.end(); I != E; ++I) {
- if (SrcNames[I->first].first == nullptr)
+ for (const auto &Entry : DstNames) {
+ if (SrcNames[Entry.first].first == nullptr)
Pattern->error("Pattern has input without matching name in output: $" +
- I->first);
+ Entry.first);
}
// Scan all of the named values in the source pattern, rejecting them if the
// name isn't used in the dest, and isn't used to tie two values together.
- for (std::map<std::string, NameRecord>::iterator
- I = SrcNames.begin(), E = SrcNames.end(); I != E; ++I)
- if (DstNames[I->first].first == nullptr && SrcNames[I->first].second == 1)
- Pattern->error("Pattern has dead named input: $" + I->first);
+ for (const auto &Entry : SrcNames)
+ if (DstNames[Entry.first].first == nullptr &&
+ SrcNames[Entry.first].second == 1)
+ Pattern->error("Pattern has dead named input: $" + Entry.first);
PatternsToMatch.push_back(PTM);
}
@@ -3258,31 +3300,29 @@ void CodeGenDAGPatterns::InferInstructionFlags() {
// Revisit instructions with undefined flags and no pattern.
if (Target.guessInstructionProperties()) {
- for (unsigned i = 0, e = Revisit.size(); i != e; ++i) {
- CodeGenInstruction &InstInfo = *Revisit[i];
- if (InstInfo.InferredFrom)
+ for (CodeGenInstruction *InstInfo : Revisit) {
+ if (InstInfo->InferredFrom)
continue;
// The mayLoad and mayStore flags default to false.
// Conservatively assume hasSideEffects if it wasn't explicit.
- if (InstInfo.hasSideEffects_Unset)
- InstInfo.hasSideEffects = true;
+ if (InstInfo->hasSideEffects_Unset)
+ InstInfo->hasSideEffects = true;
}
return;
}
// Complain about any flags that are still undefined.
- for (unsigned i = 0, e = Revisit.size(); i != e; ++i) {
- CodeGenInstruction &InstInfo = *Revisit[i];
- if (InstInfo.InferredFrom)
+ for (CodeGenInstruction *InstInfo : Revisit) {
+ if (InstInfo->InferredFrom)
continue;
- if (InstInfo.hasSideEffects_Unset)
- PrintError(InstInfo.TheDef->getLoc(),
+ if (InstInfo->hasSideEffects_Unset)
+ PrintError(InstInfo->TheDef->getLoc(),
"Can't infer hasSideEffects from patterns");
- if (InstInfo.mayStore_Unset)
- PrintError(InstInfo.TheDef->getLoc(),
+ if (InstInfo->mayStore_Unset)
+ PrintError(InstInfo->TheDef->getLoc(),
"Can't infer mayStore from patterns");
- if (InstInfo.mayLoad_Unset)
- PrintError(InstInfo.TheDef->getLoc(),
+ if (InstInfo->mayLoad_Unset)
+ PrintError(InstInfo->TheDef->getLoc(),
"Can't infer mayLoad from patterns");
}
}
@@ -3302,8 +3342,8 @@ void CodeGenDAGPatterns::VerifyInstructionFlags() {
unsigned NumSideEffects = 0;
unsigned NumStores = 0;
unsigned NumLoads = 0;
- for (unsigned i = 0, e = Instrs.size(); i != e; ++i) {
- const CodeGenInstruction &InstInfo = Target.getInstruction(Instrs[i]);
+ for (const Record *Instr : Instrs) {
+ const CodeGenInstruction &InstInfo = Target.getInstruction(Instr);
NumSideEffects += InstInfo.hasSideEffects;
NumStores += InstInfo.mayStore;
NumLoads += InstInfo.mayLoad;
@@ -3335,19 +3375,19 @@ void CodeGenDAGPatterns::VerifyInstructionFlags() {
continue;
++Errors;
- for (unsigned i = 0, e = Msgs.size(); i != e; ++i)
- PrintError(PTM.getSrcRecord()->getLoc(), Twine(Msgs[i]) + " on the " +
+ for (const std::string &Msg : Msgs)
+ PrintError(PTM.getSrcRecord()->getLoc(), Twine(Msg) + " on the " +
(Instrs.size() == 1 ?
"instruction" : "output instructions"));
// Provide the location of the relevant instruction definitions.
- for (unsigned i = 0, e = Instrs.size(); i != e; ++i) {
- if (Instrs[i] != PTM.getSrcRecord())
- PrintError(Instrs[i]->getLoc(), "defined here");
- const CodeGenInstruction &InstInfo = Target.getInstruction(Instrs[i]);
+ for (const Record *Instr : Instrs) {
+ if (Instr != PTM.getSrcRecord())
+ PrintError(Instr->getLoc(), "defined here");
+ const CodeGenInstruction &InstInfo = Target.getInstruction(Instr);
if (InstInfo.InferredFrom &&
InstInfo.InferredFrom != InstInfo.TheDef &&
InstInfo.InferredFrom != PTM.getSrcRecord())
- PrintError(InstInfo.InferredFrom->getLoc(), "inferred from patttern");
+ PrintError(InstInfo.InferredFrom->getLoc(), "inferred from pattern");
}
}
if (Errors)
@@ -3386,8 +3426,7 @@ static bool ForceArbitraryInstResultType(TreePatternNode *N, TreePattern &TP) {
void CodeGenDAGPatterns::ParsePatterns() {
std::vector<Record*> Patterns = Records.getAllDerivedDefinitions("Pattern");
- for (unsigned i = 0, e = Patterns.size(); i != e; ++i) {
- Record *CurPattern = Patterns[i];
+ for (Record *CurPattern : Patterns) {
DagInit *Tree = CurPattern->getValueAsDag("PatternToMatch");
// If the pattern references the null_frag, there's nothing to do.
@@ -3517,8 +3556,8 @@ static void CombineChildVariants(TreePatternNode *Orig,
CodeGenDAGPatterns &CDP,
const MultipleUseVarSet &DepVars) {
// Make sure that each operand has at least one variant to choose from.
- for (unsigned i = 0, e = ChildVariants.size(); i != e; ++i)
- if (ChildVariants[i].empty())
+ for (const auto &Variants : ChildVariants)
+ if (Variants.empty())
return;
// The end result is an all-pairs construction of the resultant pattern.
@@ -3529,8 +3568,8 @@ static void CombineChildVariants(TreePatternNode *Orig,
#ifndef NDEBUG
DEBUG(if (!Idxs.empty()) {
errs() << Orig->getOperator()->getName() << ": Idxs = [ ";
- for (unsigned i = 0; i < Idxs.size(); ++i) {
- errs() << Idxs[i] << " ";
+ for (unsigned Idx : Idxs) {
+ errs() << Idx << " ";
}
errs() << "]\n";
});
@@ -3539,8 +3578,8 @@ static void CombineChildVariants(TreePatternNode *Orig,
std::vector<TreePatternNode*> NewChildren;
for (unsigned i = 0, e = ChildVariants.size(); i != e; ++i)
NewChildren.push_back(ChildVariants[i][Idxs[i]]);
- TreePatternNode *R = new TreePatternNode(Orig->getOperator(), NewChildren,
- Orig->getNumTypes());
+ auto R = llvm::make_unique<TreePatternNode>(
+ Orig->getOperator(), NewChildren, Orig->getNumTypes());
// Copy over properties.
R->setName(Orig->getName());
@@ -3551,29 +3590,19 @@ static void CombineChildVariants(TreePatternNode *Orig,
// If this pattern cannot match, do not include it as a variant.
std::string ErrString;
- if (!R->canPatternMatch(ErrString, CDP)) {
- delete R;
- } else {
- bool AlreadyExists = false;
-
- // Scan to see if this pattern has already been emitted. We can get
- // duplication due to things like commuting:
- // (and GPRC:$a, GPRC:$b) -> (and GPRC:$b, GPRC:$a)
- // which are the same pattern. Ignore the dups.
- for (unsigned i = 0, e = OutVariants.size(); i != e; ++i)
- if (R->isIsomorphicTo(OutVariants[i], DepVars)) {
- AlreadyExists = true;
- break;
- }
-
- if (AlreadyExists)
- delete R;
- else
- OutVariants.push_back(R);
- }
+ // Scan to see if this pattern has already been emitted. We can get
+ // duplication due to things like commuting:
+ // (and GPRC:$a, GPRC:$b) -> (and GPRC:$b, GPRC:$a)
+ // which are the same pattern. Ignore the dups.
+ if (R->canPatternMatch(ErrString, CDP) &&
+ std::none_of(OutVariants.begin(), OutVariants.end(),
+ [&](TreePatternNode *Variant) {
+ return R->isIsomorphicTo(Variant, DepVars);
+ }))
+ OutVariants.push_back(R.release());
// Increment indices to the next permutation by incrementing the
- // indicies from last index backward, e.g., generate the sequence
+ // indices from last index backward, e.g., generate the sequence
// [0, 0], [0, 1], [1, 0], [1, 1].
int IdxsIdx;
for (IdxsIdx = Idxs.size() - 1; IdxsIdx >= 0; --IdxsIdx) {
@@ -3724,7 +3753,7 @@ static void GenerateVariantsOf(TreePatternNode *N,
// operands are the commutative operands, and there might be more operands
// after those.
assert(NC >= 3 &&
- "Commutative intrinsic should have at least 3 childrean!");
+ "Commutative intrinsic should have at least 3 children!");
std::vector<std::vector<TreePatternNode*> > Variants;
Variants.push_back(ChildVariants[0]); // Intrinsic id.
Variants.push_back(ChildVariants[2]);
diff --git a/utils/TableGen/CodeGenDAGPatterns.h b/utils/TableGen/CodeGenDAGPatterns.h
index 9ce3cdfd7bc15..76c9cefea50fd 100644
--- a/utils/TableGen/CodeGenDAGPatterns.h
+++ b/utils/TableGen/CodeGenDAGPatterns.h
@@ -132,22 +132,25 @@ namespace EEVT {
/// this an other based on this information.
bool EnforceSmallerThan(EEVT::TypeSet &Other, TreePattern &TP);
- /// EnforceVectorEltTypeIs - 'this' is now constrainted to be a vector type
+ /// EnforceVectorEltTypeIs - 'this' is now constrained to be a vector type
/// whose element is VT.
bool EnforceVectorEltTypeIs(EEVT::TypeSet &VT, TreePattern &TP);
- /// EnforceVectorEltTypeIs - 'this' is now constrainted to be a vector type
+ /// EnforceVectorEltTypeIs - 'this' is now constrained to be a vector type
/// whose element is VT.
bool EnforceVectorEltTypeIs(MVT::SimpleValueType VT, TreePattern &TP);
- /// EnforceVectorSubVectorTypeIs - 'this' is now constrainted to
+ /// EnforceVectorSubVectorTypeIs - 'this' is now constrained to
/// be a vector type VT.
bool EnforceVectorSubVectorTypeIs(EEVT::TypeSet &VT, TreePattern &TP);
- /// EnforceVectorSameNumElts - 'this' is now constrainted to
+ /// EnforceVectorSameNumElts - 'this' is now constrained to
/// be a vector with same num elements as VT.
bool EnforceVectorSameNumElts(EEVT::TypeSet &VT, TreePattern &TP);
+ /// EnforceSameSize - 'this' is now constrained to be the same size as VT.
+ bool EnforceSameSize(EEVT::TypeSet &VT, TreePattern &TP);
+
bool operator!=(const TypeSet &RHS) const { return TypeVec != RHS.TypeVec; }
bool operator==(const TypeSet &RHS) const { return TypeVec == RHS.TypeVec; }
@@ -173,7 +176,7 @@ struct SDTypeConstraint {
enum {
SDTCisVT, SDTCisPtrTy, SDTCisInt, SDTCisFP, SDTCisVec, SDTCisSameAs,
SDTCisVTSmallerThanOp, SDTCisOpSmallerThanOp, SDTCisEltOfVec,
- SDTCisSubVecOfVec, SDTCVecEltisVT, SDTCisSameNumEltsAs
+ SDTCisSubVecOfVec, SDTCVecEltisVT, SDTCisSameNumEltsAs, SDTCisSameSizeAs
} ConstraintType;
union { // The discriminated union.
@@ -201,6 +204,9 @@ struct SDTypeConstraint {
struct {
unsigned OtherOperandNum;
} SDTCisSameNumEltsAs_Info;
+ struct {
+ unsigned OtherOperandNum;
+ } SDTCisSameSizeAs_Info;
} x;
/// ApplyTypeConstraint - Given a node in a pattern, apply this type
diff --git a/utils/TableGen/CodeGenInstruction.cpp b/utils/TableGen/CodeGenInstruction.cpp
index e83d5033b1826..366e8ec7fac15 100644
--- a/utils/TableGen/CodeGenInstruction.cpp
+++ b/utils/TableGen/CodeGenInstruction.cpp
@@ -78,6 +78,7 @@ CGIOperandList::CGIOperandList(Record *R) : TheDef(R) {
} else if (Rec->isSubClassOf("Operand")) {
PrintMethod = Rec->getValueAsString("PrintMethod");
OperandType = Rec->getValueAsString("OperandType");
+ OperandNamespace = Rec->getValueAsString("OperandNamespace");
// If there is an explicit encoder method, use it.
EncoderMethod = Rec->getValueAsString("EncoderMethod");
MIOpInfo = Rec->getValueAsDag("MIOperandInfo");
diff --git a/utils/TableGen/CodeGenIntrinsics.h b/utils/TableGen/CodeGenIntrinsics.h
index f4055571b1c7b..7bdb7e1bc537c 100644
--- a/utils/TableGen/CodeGenIntrinsics.h
+++ b/utils/TableGen/CodeGenIntrinsics.h
@@ -60,9 +60,10 @@ namespace llvm {
IntrinsicSignature IS;
// Memory mod/ref behavior of this intrinsic.
- enum {
+ enum ModRefKind {
NoMem, ReadArgMem, ReadMem, ReadWriteArgMem, ReadWriteMem
- } ModRef;
+ };
+ ModRefKind ModRef;
/// This is set to true if the intrinsic is overloaded by its argument
/// types.
diff --git a/utils/TableGen/CodeGenMapTable.cpp b/utils/TableGen/CodeGenMapTable.cpp
index 48df4391fe164..f66dd082709ba 100644
--- a/utils/TableGen/CodeGenMapTable.cpp
+++ b/utils/TableGen/CodeGenMapTable.cpp
@@ -471,7 +471,7 @@ void MapTableEmitter::emitTablesWithFunc(raw_ostream &OS) {
ListInit *ColFields = InstrMapDesc.getColFields();
const std::vector<ListInit*> &ValueCols = InstrMapDesc.getValueCols();
- OS << "// "<< InstrMapDesc.getName() << "\n";
+ OS << "// "<< InstrMapDesc.getName() << "\nLLVM_READONLY\n";
OS << "int "<< InstrMapDesc.getName() << "(uint16_t Opcode";
if (ValueCols.size() > 1) {
for (Init *CF : ColFields->getValues()) {
diff --git a/utils/TableGen/CodeGenRegisters.cpp b/utils/TableGen/CodeGenRegisters.cpp
index c9e6d1d379de9..ca316e96a21ad 100644
--- a/utils/TableGen/CodeGenRegisters.cpp
+++ b/utils/TableGen/CodeGenRegisters.cpp
@@ -1171,20 +1171,13 @@ void CodeGenRegBank::computeSubRegLaneMasks() {
CoveringLanes = ~0u;
for (auto &Idx : SubRegIndices) {
if (Idx.getComposites().empty()) {
+ if (Bit > 32) {
+ PrintFatalError(
+ Twine("Ran out of lanemask bits to represent subregister ")
+ + Idx.getName());
+ }
Idx.LaneMask = 1u << Bit;
- // Share bit 31 in the unlikely case there are more than 32 leafs.
- //
- // Sharing bits is harmless; it allows graceful degradation in targets
- // with more than 32 vector lanes. They simply get a limited resolution
- // view of lanes beyond the 32nd.
- //
- // See also the comment for getSubRegIndexLaneMask().
- if (Bit < 31)
- ++Bit;
- else
- // Once bit 31 is shared among multiple leafs, the 'lane' it represents
- // is no longer covering its registers.
- CoveringLanes &= ~(1u << Bit);
+ ++Bit;
} else {
Idx.LaneMask = 0;
}
@@ -1274,6 +1267,12 @@ void CodeGenRegBank::computeSubRegLaneMasks() {
continue;
LaneMask |= SubRegIndex.LaneMask;
}
+
+ // For classes without any subregisters set LaneMask to ~0u instead of 0.
+ // This makes it easier for client code to handle classes uniformly.
+ if (LaneMask == 0)
+ LaneMask = ~0u;
+
RegClass.LaneMask = LaneMask;
}
}
@@ -1568,6 +1567,12 @@ void CodeGenRegBank::pruneUnitSets() {
&& UnitWeight == RegUnits[SuperSet.Units.back()].Weight) {
DEBUG(dbgs() << "UnitSet " << SubIdx << " subsumed by " << SuperIdx
<< "\n");
+ // We can pick any of the set names for the merged set. Go for the
+ // shortest one to avoid picking the name of one of the classes that are
+ // artificially created by tablegen. So "FPR128_lo" instead of
+ // "QQQQ_with_qsub3_in_FPR128_lo".
+ if (RegUnitSets[SubIdx].Name.size() < RegUnitSets[SuperIdx].Name.size())
+ RegUnitSets[SuperIdx].Name = RegUnitSets[SubIdx].Name;
break;
}
}
diff --git a/utils/TableGen/CodeGenSchedule.cpp b/utils/TableGen/CodeGenSchedule.cpp
index bc27481869fe8..c98f62345342d 100644
--- a/utils/TableGen/CodeGenSchedule.cpp
+++ b/utils/TableGen/CodeGenSchedule.cpp
@@ -24,15 +24,9 @@ using namespace llvm;
#define DEBUG_TYPE "subtarget-emitter"
#ifndef NDEBUG
-static void dumpIdxVec(const IdxVec &V) {
- for (unsigned i = 0, e = V.size(); i < e; ++i) {
- dbgs() << V[i] << ", ";
- }
-}
-static void dumpIdxVec(const SmallVectorImpl<unsigned> &V) {
- for (unsigned i = 0, e = V.size(); i < e; ++i) {
- dbgs() << V[i] << ", ";
- }
+static void dumpIdxVec(ArrayRef<unsigned> V) {
+ for (unsigned Idx : V)
+ dbgs() << Idx << ", ";
}
#endif
@@ -326,9 +320,9 @@ void CodeGenSchedModels::collectSchedRW() {
}
/// Compute a SchedWrite name from a sequence of writes.
-std::string CodeGenSchedModels::genRWName(const IdxVec& Seq, bool IsRead) {
+std::string CodeGenSchedModels::genRWName(ArrayRef<unsigned> Seq, bool IsRead) {
std::string Name("(");
- for (IdxIter I = Seq.begin(), E = Seq.end(); I != E; ++I) {
+ for (auto I = Seq.begin(), E = Seq.end(); I != E; ++I) {
if (I != Seq.begin())
Name += '_';
Name += getSchedRW(*I, IsRead).Name;
@@ -457,13 +451,13 @@ void CodeGenSchedModels::expandRWSeqForProc(
}
// Find the existing SchedWrite that models this sequence of writes.
-unsigned CodeGenSchedModels::findRWForSequence(const IdxVec &Seq,
+unsigned CodeGenSchedModels::findRWForSequence(ArrayRef<unsigned> Seq,
bool IsRead) {
std::vector<CodeGenSchedRW> &RWVec = IsRead ? SchedReads : SchedWrites;
for (std::vector<CodeGenSchedRW>::iterator I = RWVec.begin(), E = RWVec.end();
I != E; ++I) {
- if (I->Sequence == Seq)
+ if (makeArrayRef(I->Sequence) == Seq)
return I - RWVec.begin();
}
// Index zero reserved for invalid RW.
@@ -585,11 +579,11 @@ void CodeGenSchedModels::collectSchedClasses() {
/// Find an SchedClass that has been inferred from a per-operand list of
/// SchedWrites and SchedReads.
unsigned CodeGenSchedModels::findSchedClassIdx(Record *ItinClassDef,
- const IdxVec &Writes,
- const IdxVec &Reads) const {
+ ArrayRef<unsigned> Writes,
+ ArrayRef<unsigned> Reads) const {
for (SchedClassIter I = schedClassBegin(), E = schedClassEnd(); I != E; ++I) {
- if (I->ItinClassDef == ItinClassDef
- && I->Writes == Writes && I->Reads == Reads) {
+ if (I->ItinClassDef == ItinClassDef && makeArrayRef(I->Writes) == Writes &&
+ makeArrayRef(I->Reads) == Reads) {
return I - schedClassBegin();
}
}
@@ -603,20 +597,22 @@ unsigned CodeGenSchedModels::getSchedClassIdx(
return InstrClassMap.lookup(Inst.TheDef);
}
-std::string CodeGenSchedModels::createSchedClassName(
- Record *ItinClassDef, const IdxVec &OperWrites, const IdxVec &OperReads) {
+std::string
+CodeGenSchedModels::createSchedClassName(Record *ItinClassDef,
+ ArrayRef<unsigned> OperWrites,
+ ArrayRef<unsigned> OperReads) {
std::string Name;
if (ItinClassDef && ItinClassDef->getName() != "NoItinerary")
Name = ItinClassDef->getName();
- for (IdxIter WI = OperWrites.begin(), WE = OperWrites.end(); WI != WE; ++WI) {
+ for (unsigned Idx : OperWrites) {
if (!Name.empty())
Name += '_';
- Name += SchedWrites[*WI].Name;
+ Name += SchedWrites[Idx].Name;
}
- for (IdxIter RI = OperReads.begin(), RE = OperReads.end(); RI != RE; ++RI) {
+ for (unsigned Idx : OperReads) {
Name += '_';
- Name += SchedReads[*RI].Name;
+ Name += SchedReads[Idx].Name;
}
return Name;
}
@@ -636,10 +632,9 @@ std::string CodeGenSchedModels::createSchedClassName(const RecVec &InstDefs) {
/// SchedWrites and SchedReads. ProcIndices contains the set of IDs of
/// processors that may utilize this class.
unsigned CodeGenSchedModels::addSchedClass(Record *ItinClassDef,
- const IdxVec &OperWrites,
- const IdxVec &OperReads,
- const IdxVec &ProcIndices)
-{
+ ArrayRef<unsigned> OperWrites,
+ ArrayRef<unsigned> OperReads,
+ ArrayRef<unsigned> ProcIndices) {
assert(!ProcIndices.empty() && "expect at least one ProcIdx");
unsigned Idx = findSchedClassIdx(ItinClassDef, OperWrites, OperReads);
@@ -1322,10 +1317,10 @@ static void inferFromTransitions(ArrayRef<PredTransition> LastTransitions,
// Create new SchedClasses for the given ReadWrite list. If any of the
// ReadWrites refers to a SchedVariant, create a new SchedClass for each variant
// of the ReadWrite list, following Aliases if necessary.
-void CodeGenSchedModels::inferFromRW(const IdxVec &OperWrites,
- const IdxVec &OperReads,
+void CodeGenSchedModels::inferFromRW(ArrayRef<unsigned> OperWrites,
+ ArrayRef<unsigned> OperReads,
unsigned FromClassIdx,
- const IdxVec &ProcIndices) {
+ ArrayRef<unsigned> ProcIndices) {
DEBUG(dbgs() << "INFER RW proc("; dumpIdxVec(ProcIndices); dbgs() << ") ");
// Create a seed transition with an empty PredTerm and the expanded sequences
@@ -1335,9 +1330,9 @@ void CodeGenSchedModels::inferFromRW(const IdxVec &OperWrites,
LastTransitions.back().ProcIndices.append(ProcIndices.begin(),
ProcIndices.end());
- for (IdxIter I = OperWrites.begin(), E = OperWrites.end(); I != E; ++I) {
+ for (unsigned WriteIdx : OperWrites) {
IdxVec WriteSeq;
- expandRWSequence(*I, WriteSeq, /*IsRead=*/false);
+ expandRWSequence(WriteIdx, WriteSeq, /*IsRead=*/false);
unsigned Idx = LastTransitions[0].WriteSequences.size();
LastTransitions[0].WriteSequences.resize(Idx + 1);
SmallVectorImpl<unsigned> &Seq = LastTransitions[0].WriteSequences[Idx];
@@ -1346,9 +1341,9 @@ void CodeGenSchedModels::inferFromRW(const IdxVec &OperWrites,
DEBUG(dbgs() << "("; dumpIdxVec(Seq); dbgs() << ") ");
}
DEBUG(dbgs() << " Reads: ");
- for (IdxIter I = OperReads.begin(), E = OperReads.end(); I != E; ++I) {
+ for (unsigned ReadIdx : OperReads) {
IdxVec ReadSeq;
- expandRWSequence(*I, ReadSeq, /*IsRead=*/true);
+ expandRWSequence(ReadIdx, ReadSeq, /*IsRead=*/true);
unsigned Idx = LastTransitions[0].ReadSequences.size();
LastTransitions[0].ReadSequences.resize(Idx + 1);
SmallVectorImpl<unsigned> &Seq = LastTransitions[0].ReadSequences[Idx];
@@ -1552,20 +1547,16 @@ void CodeGenSchedModels::collectItinProcResources(Record *ItinClassDef) {
}
void CodeGenSchedModels::collectRWResources(unsigned RWIdx, bool IsRead,
- const IdxVec &ProcIndices) {
+ ArrayRef<unsigned> ProcIndices) {
const CodeGenSchedRW &SchedRW = getSchedRW(RWIdx, IsRead);
if (SchedRW.TheDef) {
if (!IsRead && SchedRW.TheDef->isSubClassOf("SchedWriteRes")) {
- for (IdxIter PI = ProcIndices.begin(), PE = ProcIndices.end();
- PI != PE; ++PI) {
- addWriteRes(SchedRW.TheDef, *PI);
- }
+ for (unsigned Idx : ProcIndices)
+ addWriteRes(SchedRW.TheDef, Idx);
}
else if (IsRead && SchedRW.TheDef->isSubClassOf("SchedReadAdvance")) {
- for (IdxIter PI = ProcIndices.begin(), PE = ProcIndices.end();
- PI != PE; ++PI) {
- addReadAdvance(SchedRW.TheDef, *PI);
- }
+ for (unsigned Idx : ProcIndices)
+ addReadAdvance(SchedRW.TheDef, Idx);
}
}
for (RecIter AI = SchedRW.Aliases.begin(), AE = SchedRW.Aliases.end();
@@ -1590,15 +1581,15 @@ void CodeGenSchedModels::collectRWResources(unsigned RWIdx, bool IsRead,
}
// Collect resources for a set of read/write types and processor indices.
-void CodeGenSchedModels::collectRWResources(const IdxVec &Writes,
- const IdxVec &Reads,
- const IdxVec &ProcIndices) {
+void CodeGenSchedModels::collectRWResources(ArrayRef<unsigned> Writes,
+ ArrayRef<unsigned> Reads,
+ ArrayRef<unsigned> ProcIndices) {
- for (IdxIter WI = Writes.begin(), WE = Writes.end(); WI != WE; ++WI)
- collectRWResources(*WI, /*IsRead=*/false, ProcIndices);
+ for (unsigned Idx : Writes)
+ collectRWResources(Idx, /*IsRead=*/false, ProcIndices);
- for (IdxIter RI = Reads.begin(), RE = Reads.end(); RI != RE; ++RI)
- collectRWResources(*RI, /*IsRead=*/true, ProcIndices);
+ for (unsigned Idx : Reads)
+ collectRWResources(Idx, /*IsRead=*/true, ProcIndices);
}
diff --git a/utils/TableGen/CodeGenSchedule.h b/utils/TableGen/CodeGenSchedule.h
index e5241b9d5fda6..f5c50c992a928 100644
--- a/utils/TableGen/CodeGenSchedule.h
+++ b/utils/TableGen/CodeGenSchedule.h
@@ -72,10 +72,10 @@ struct CodeGenSchedRW {
IsSequence = Def->isSubClassOf("WriteSequence");
}
- CodeGenSchedRW(unsigned Idx, bool Read, const IdxVec &Seq,
+ CodeGenSchedRW(unsigned Idx, bool Read, ArrayRef<unsigned> Seq,
const std::string &Name)
- : Index(Idx), Name(Name), TheDef(nullptr), IsRead(Read), IsAlias(false),
- HasVariants(false), IsVariadic(false), IsSequence(true), Sequence(Seq) {
+ : Index(Idx), Name(Name), TheDef(nullptr), IsRead(Read), IsAlias(false),
+ HasVariants(false), IsVariadic(false), IsSequence(true), Sequence(Seq) {
assert(Sequence.size() > 1 && "implied sequence needs >1 RWs");
}
@@ -144,8 +144,9 @@ struct CodeGenSchedClass {
CodeGenSchedClass(): Index(0), ItinClassDef(nullptr) {}
- bool isKeyEqual(Record *IC, const IdxVec &W, const IdxVec &R) {
- return ItinClassDef == IC && Writes == W && Reads == R;
+ bool isKeyEqual(Record *IC, ArrayRef<unsigned> W, ArrayRef<unsigned> R) {
+ return ItinClassDef == IC && makeArrayRef(Writes) == W &&
+ makeArrayRef(Reads) == R;
}
// Is this class generated from a variants if existing classes? Instructions
@@ -256,18 +257,16 @@ public:
class_iterator classes_end() { return SchedClasses.end(); }
const_class_iterator classes_end() const { return SchedClasses.end(); }
iterator_range<class_iterator> classes() {
- return iterator_range<class_iterator>(classes_begin(), classes_end());
+ return make_range(classes_begin(), classes_end());
}
iterator_range<const_class_iterator> classes() const {
- return iterator_range<const_class_iterator>(classes_begin(), classes_end());
+ return make_range(classes_begin(), classes_end());
}
iterator_range<class_iterator> explicit_classes() {
- return iterator_range<class_iterator>(
- classes_begin(), classes_begin() + NumInstrSchedClasses);
+ return make_range(classes_begin(), classes_begin() + NumInstrSchedClasses);
}
iterator_range<const_class_iterator> explicit_classes() const {
- return iterator_range<const_class_iterator>(
- classes_begin(), classes_begin() + NumInstrSchedClasses);
+ return make_range(classes_begin(), classes_begin() + NumInstrSchedClasses);
}
Record *getModelOrItinDef(Record *ProcDef) const {
@@ -363,14 +362,14 @@ public:
void expandRWSeqForProc(unsigned RWIdx, IdxVec &RWSeq, bool IsRead,
const CodeGenProcModel &ProcModel) const;
- unsigned addSchedClass(Record *ItinDef, const IdxVec &OperWrites,
- const IdxVec &OperReads, const IdxVec &ProcIndices);
+ unsigned addSchedClass(Record *ItinDef, ArrayRef<unsigned> OperWrites,
+ ArrayRef<unsigned> OperReads,
+ ArrayRef<unsigned> ProcIndices);
unsigned findOrInsertRW(ArrayRef<unsigned> Seq, bool IsRead);
- unsigned findSchedClassIdx(Record *ItinClassDef,
- const IdxVec &Writes,
- const IdxVec &Reads) const;
+ unsigned findSchedClassIdx(Record *ItinClassDef, ArrayRef<unsigned> Writes,
+ ArrayRef<unsigned> Reads) const;
Record *findProcResUnits(Record *ProcResKind,
const CodeGenProcModel &PM) const;
@@ -383,14 +382,14 @@ private:
void collectSchedRW();
- std::string genRWName(const IdxVec& Seq, bool IsRead);
- unsigned findRWForSequence(const IdxVec &Seq, bool IsRead);
+ std::string genRWName(ArrayRef<unsigned> Seq, bool IsRead);
+ unsigned findRWForSequence(ArrayRef<unsigned> Seq, bool IsRead);
void collectSchedClasses();
std::string createSchedClassName(Record *ItinClassDef,
- const IdxVec &OperWrites,
- const IdxVec &OperReads);
+ ArrayRef<unsigned> OperWrites,
+ ArrayRef<unsigned> OperReads);
std::string createSchedClassName(const RecVec &InstDefs);
void createInstRWClass(Record *InstRWDef);
@@ -400,8 +399,8 @@ private:
void inferSchedClasses();
- void inferFromRW(const IdxVec &OperWrites, const IdxVec &OperReads,
- unsigned FromClassIdx, const IdxVec &ProcIndices);
+ void inferFromRW(ArrayRef<unsigned> OperWrites, ArrayRef<unsigned> OperReads,
+ unsigned FromClassIdx, ArrayRef<unsigned> ProcIndices);
void inferFromItinClass(Record *ItinClassDef, unsigned FromClassIdx);
void inferFromInstRWs(unsigned SCIdx);
@@ -413,10 +412,10 @@ private:
void collectItinProcResources(Record *ItinClassDef);
void collectRWResources(unsigned RWIdx, bool IsRead,
- const IdxVec &ProcIndices);
+ ArrayRef<unsigned> ProcIndices);
- void collectRWResources(const IdxVec &Writes, const IdxVec &Reads,
- const IdxVec &ProcIndices);
+ void collectRWResources(ArrayRef<unsigned> Writes, ArrayRef<unsigned> Reads,
+ ArrayRef<unsigned> ProcIndices);
void addProcResource(Record *ProcResourceKind, CodeGenProcModel &PM);
diff --git a/utils/TableGen/CodeGenTarget.cpp b/utils/TableGen/CodeGenTarget.cpp
index 661975ecb202e..aaad4225ace88 100644
--- a/utils/TableGen/CodeGenTarget.cpp
+++ b/utils/TableGen/CodeGenTarget.cpp
@@ -76,6 +76,8 @@ std::string llvm::getEnumName(MVT::SimpleValueType T) {
case MVT::v16i1: return "MVT::v16i1";
case MVT::v32i1: return "MVT::v32i1";
case MVT::v64i1: return "MVT::v64i1";
+ case MVT::v512i1: return "MVT::v512i1";
+ case MVT::v1024i1: return "MVT::v1024i1";
case MVT::v1i8: return "MVT::v1i8";
case MVT::v2i8: return "MVT::v2i8";
case MVT::v4i8: return "MVT::v4i8";
@@ -83,22 +85,29 @@ std::string llvm::getEnumName(MVT::SimpleValueType T) {
case MVT::v16i8: return "MVT::v16i8";
case MVT::v32i8: return "MVT::v32i8";
case MVT::v64i8: return "MVT::v64i8";
+ case MVT::v128i8: return "MVT::v128i8";
+ case MVT::v256i8: return "MVT::v256i8";
case MVT::v1i16: return "MVT::v1i16";
case MVT::v2i16: return "MVT::v2i16";
case MVT::v4i16: return "MVT::v4i16";
case MVT::v8i16: return "MVT::v8i16";
case MVT::v16i16: return "MVT::v16i16";
case MVT::v32i16: return "MVT::v32i16";
+ case MVT::v64i16: return "MVT::v64i16";
+ case MVT::v128i16: return "MVT::v128i16";
case MVT::v1i32: return "MVT::v1i32";
case MVT::v2i32: return "MVT::v2i32";
case MVT::v4i32: return "MVT::v4i32";
case MVT::v8i32: return "MVT::v8i32";
case MVT::v16i32: return "MVT::v16i32";
+ case MVT::v32i32: return "MVT::v32i32";
+ case MVT::v64i32: return "MVT::v64i32";
case MVT::v1i64: return "MVT::v1i64";
case MVT::v2i64: return "MVT::v2i64";
case MVT::v4i64: return "MVT::v4i64";
case MVT::v8i64: return "MVT::v8i64";
case MVT::v16i64: return "MVT::v16i64";
+ case MVT::v32i64: return "MVT::v32i64";
case MVT::v1i128: return "MVT::v1i128";
case MVT::v2f16: return "MVT::v2f16";
case MVT::v4f16: return "MVT::v4f16";
@@ -112,6 +121,7 @@ std::string llvm::getEnumName(MVT::SimpleValueType T) {
case MVT::v2f64: return "MVT::v2f64";
case MVT::v4f64: return "MVT::v4f64";
case MVT::v8f64: return "MVT::v8f64";
+ case MVT::token: return "MVT::token";
case MVT::Metadata: return "MVT::Metadata";
case MVT::iPTR: return "MVT::iPTR";
case MVT::iPTRAny: return "MVT::iPTRAny";
@@ -252,7 +262,7 @@ void CodeGenTarget::ReadLegalValueTypes() const {
LegalValueTypes.insert(LegalValueTypes.end(), RC.VTs.begin(), RC.VTs.end());
// Remove duplicates.
- std::sort(LegalValueTypes.begin(), LegalValueTypes.end());
+ array_pod_sort(LegalValueTypes.begin(), LegalValueTypes.end());
LegalValueTypes.erase(std::unique(LegalValueTypes.begin(),
LegalValueTypes.end()),
LegalValueTypes.end());
diff --git a/utils/TableGen/CodeGenTarget.h b/utils/TableGen/CodeGenTarget.h
index 24b38514260c3..cf4a0bbe5bd9e 100644
--- a/utils/TableGen/CodeGenTarget.h
+++ b/utils/TableGen/CodeGenTarget.h
@@ -173,7 +173,7 @@ public:
inst_iterator inst_begin() const{return getInstructionsByEnumValue().begin();}
inst_iterator inst_end() const { return getInstructionsByEnumValue().end(); }
iterator_range<inst_iterator> instructions() const {
- return iterator_range<inst_iterator>(inst_begin(), inst_end());
+ return make_range(inst_begin(), inst_end());
}
diff --git a/utils/TableGen/DAGISelMatcherEmitter.cpp b/utils/TableGen/DAGISelMatcherEmitter.cpp
index 4659dc157338b..26f53dca63618 100644
--- a/utils/TableGen/DAGISelMatcherEmitter.cpp
+++ b/utils/TableGen/DAGISelMatcherEmitter.cpp
@@ -16,6 +16,7 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/TinyPtrVector.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/TableGen/Record.h"
@@ -36,6 +37,10 @@ class MatcherTableEmitter {
DenseMap<TreePattern *, unsigned> NodePredicateMap;
std::vector<TreePredicateFn> NodePredicates;
+
+ // We de-duplicate the predicates by code string, and use this map to track
+ // all the patterns with "identical" predicates.
+ StringMap<TinyPtrVector<TreePattern *>> NodePredicatesByCodeToRun;
StringMap<unsigned> PatternPredicateMap;
std::vector<std::string> PatternPredicates;
@@ -62,10 +67,23 @@ private:
formatted_raw_ostream &OS);
unsigned getNodePredicate(TreePredicateFn Pred) {
- unsigned &Entry = NodePredicateMap[Pred.getOrigPatFragRecord()];
+ TreePattern *TP = Pred.getOrigPatFragRecord();
+ unsigned &Entry = NodePredicateMap[TP];
if (Entry == 0) {
- NodePredicates.push_back(Pred);
- Entry = NodePredicates.size();
+ TinyPtrVector<TreePattern *> &SameCodePreds =
+ NodePredicatesByCodeToRun[Pred.getCodeToRunOnSDNode()];
+ if (SameCodePreds.empty()) {
+ // We've never seen a predicate with the same code: allocate an entry.
+ NodePredicates.push_back(Pred);
+ Entry = NodePredicates.size();
+ } else {
+ // We did see an identical predicate: re-use it.
+ Entry = NodePredicateMap[SameCodePreds.front()];
+ assert(Entry != 0);
+ }
+ // In both cases, we've never seen this particular predicate before, so
+ // mark it in the list of predicates sharing the same code.
+ SameCodePreds.push_back(TP);
}
return Entry-1;
}
@@ -625,13 +643,6 @@ void MatcherTableEmitter::EmitPredicateFunctions(formatted_raw_ostream &OS) {
}
// Emit Node predicates.
- // FIXME: Annoyingly, these are stored by name, which we never even emit. Yay?
- StringMap<TreePattern*> PFsByName;
-
- for (CodeGenDAGPatterns::pf_iterator I = CGP.pf_begin(), E = CGP.pf_end();
- I != E; ++I)
- PFsByName[I->first->getName()] = I->second.get();
-
if (!NodePredicates.empty()) {
OS << "bool CheckNodePredicate(SDNode *Node,\n";
OS << " unsigned PredNo) const override {\n";
@@ -642,7 +653,10 @@ void MatcherTableEmitter::EmitPredicateFunctions(formatted_raw_ostream &OS) {
TreePredicateFn PredFn = NodePredicates[i];
assert(!PredFn.isAlwaysTrue() && "No code in this predicate");
- OS << " case " << i << ": { // " << NodePredicates[i].getFnName() <<'\n';
+ OS << " case " << i << ": { \n";
+ for (auto *SimilarPred :
+ NodePredicatesByCodeToRun[PredFn.getCodeToRunOnSDNode()])
+ OS << " // " << TreePredicateFn(SimilarPred).getFnName() <<'\n';
OS << PredFn.getCodeToRunOnSDNode() << "\n }\n";
}
diff --git a/utils/TableGen/DFAPacketizerEmitter.cpp b/utils/TableGen/DFAPacketizerEmitter.cpp
index 5060b6e9ce7c0..77afff7ab5c23 100644
--- a/utils/TableGen/DFAPacketizerEmitter.cpp
+++ b/utils/TableGen/DFAPacketizerEmitter.cpp
@@ -15,16 +15,83 @@
//
//===----------------------------------------------------------------------===//
+#define DEBUG_TYPE "dfa-emitter"
+
#include "CodeGenTarget.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringExtras.h"
#include "llvm/TableGen/Record.h"
#include "llvm/TableGen/TableGenBackend.h"
+#include "llvm/Support/Debug.h"
#include <list>
#include <map>
#include <string>
+#include <queue>
using namespace llvm;
+// --------------------------------------------------------------------
+// Definitions shared between DFAPacketizer.cpp and DFAPacketizerEmitter.cpp
+
+// DFA_MAX_RESTERMS * DFA_MAX_RESOURCES must fit within sizeof DFAInput.
+// This is verified in DFAPacketizer.cpp:DFAPacketizer::DFAPacketizer.
+//
+// e.g. terms x resource bit combinations that fit in uint32_t:
+// 4 terms x 8 bits = 32 bits
+// 3 terms x 10 bits = 30 bits
+// 2 terms x 16 bits = 32 bits
+//
+// e.g. terms x resource bit combinations that fit in uint64_t:
+// 8 terms x 8 bits = 64 bits
+// 7 terms x 9 bits = 63 bits
+// 6 terms x 10 bits = 60 bits
+// 5 terms x 12 bits = 60 bits
+// 4 terms x 16 bits = 64 bits <--- current
+// 3 terms x 21 bits = 63 bits
+// 2 terms x 32 bits = 64 bits
+//
+#define DFA_MAX_RESTERMS 4 // The max # of AND'ed resource terms.
+#define DFA_MAX_RESOURCES 16 // The max # of resource bits in one term.
+
+typedef uint64_t DFAInput;
+typedef int64_t DFAStateInput;
+#define DFA_TBLTYPE "int64_t" // For generating DFAStateInputTable.
+
+namespace {
+ DFAInput addDFAFuncUnits(DFAInput Inp, unsigned FuncUnits) {
+ return (Inp << DFA_MAX_RESOURCES) | FuncUnits;
+ }
+
+ /// Return the DFAInput for an instruction class input vector.
+ /// This function is used in both DFAPacketizer.cpp and in
+ /// DFAPacketizerEmitter.cpp.
+ DFAInput getDFAInsnInput(const std::vector<unsigned> &InsnClass) {
+ DFAInput InsnInput = 0;
+ assert ((InsnClass.size() <= DFA_MAX_RESTERMS) &&
+ "Exceeded maximum number of DFA terms");
+ for (auto U : InsnClass)
+ InsnInput = addDFAFuncUnits(InsnInput, U);
+ return InsnInput;
+ }
+}
+// --------------------------------------------------------------------
+
+#ifndef NDEBUG
+// To enable debugging, run llvm-tblgen with: "-debug-only dfa-emitter".
+//
+// dbgsInsnClass - When debugging, print instruction class stages.
+//
+void dbgsInsnClass(const std::vector<unsigned> &InsnClass);
+//
+// dbgsStateInfo - When debugging, print the set of state info.
+//
+void dbgsStateInfo(const std::set<unsigned> &stateInfo);
+//
+// dbgsIndent - When debugging, indent by the specified amount.
+//
+void dbgsIndent(unsigned indent);
+#endif
+
//
// class DFAPacketizerEmitter: class that generates and prints out the DFA
// for resource tracking.
@@ -37,20 +104,48 @@ private:
// allInsnClasses is the set of all possible resources consumed by an
// InstrStage.
//
- DenseSet<unsigned> allInsnClasses;
+ std::vector<std::vector<unsigned>> allInsnClasses;
RecordKeeper &Records;
public:
DFAPacketizerEmitter(RecordKeeper &R);
//
- // collectAllInsnClasses: Populate allInsnClasses which is a set of units
+ // collectAllFuncUnits - Construct a map of function unit names to bits.
+ //
+ int collectAllFuncUnits(std::vector<Record*> &ProcItinList,
+ std::map<std::string, unsigned> &FUNameToBitsMap,
+ int &maxResources,
+ raw_ostream &OS);
+
+ //
+ // collectAllComboFuncs - Construct a map from a combo function unit bit to
+ // the bits of all included functional units.
+ //
+ int collectAllComboFuncs(std::vector<Record*> &ComboFuncList,
+ std::map<std::string, unsigned> &FUNameToBitsMap,
+ std::map<unsigned, unsigned> &ComboBitToBitsMap,
+ raw_ostream &OS);
+
+ //
+ // collectOneInsnClass - Populate allInsnClasses with one instruction class.
+ //
+ int collectOneInsnClass(const std::string &ProcName,
+ std::vector<Record*> &ProcItinList,
+ std::map<std::string, unsigned> &FUNameToBitsMap,
+ Record *ItinData,
+ raw_ostream &OS);
+
+ //
+ // collectAllInsnClasses - Populate allInsnClasses which is a set of units
// used in each stage.
//
- void collectAllInsnClasses(const std::string &Name,
- Record *ItinData,
- unsigned &NStages,
- raw_ostream &OS);
+ int collectAllInsnClasses(const std::string &ProcName,
+ std::vector<Record*> &ProcItinList,
+ std::map<std::string, unsigned> &FUNameToBitsMap,
+ std::vector<Record*> &ItinDataList,
+ int &maxStages,
+ raw_ostream &OS);
void run(raw_ostream &OS);
};
@@ -87,7 +182,7 @@ class State {
const int stateNum;
mutable bool isInitial;
mutable std::set<unsigned> stateInfo;
- typedef std::map<unsigned, const State *> TransitionMap;
+ typedef std::map<std::vector<unsigned>, const State *> TransitionMap;
mutable TransitionMap Transitions;
State();
@@ -97,28 +192,47 @@ class State {
}
//
- // canAddInsnClass - Returns true if an instruction of type InsnClass is a
- // valid transition from this state, i.e., can an instruction of type InsnClass
- // be added to the packet represented by this state.
+ // canMaybeAddInsnClass - Quickly verifies if an instruction of type InsnClass
+ // may be a valid transition from this state i.e., can an instruction of type
+ // InsnClass be added to the packet represented by this state.
+ //
+ // Note that for multiple stages, this quick check does not take into account
+ // any possible resource competition between the stages themselves. That is
+ // enforced in AddInsnClassStages which checks the cross product of all
+ // stages for resource availability (which is a more involved check).
+ //
+ bool canMaybeAddInsnClass(std::vector<unsigned> &InsnClass,
+ std::map<unsigned, unsigned> &ComboBitToBitsMap) const;
+ //
+ // AddInsnClass - Return all combinations of resource reservation
+ // which are possible from this state (PossibleStates).
//
// PossibleStates is the set of valid resource states that ensue from valid
// transitions.
//
- bool canAddInsnClass(unsigned InsnClass) const;
+ void AddInsnClass(std::vector<unsigned> &InsnClass,
+ std::map<unsigned, unsigned> &ComboBitToBitsMap,
+ std::set<unsigned> &PossibleStates) const;
//
- // AddInsnClass - Return all combinations of resource reservation
+ // AddInsnClassStages - Return all combinations of resource reservation
+ // resulting from the cross product of all stages for this InsnClass
// which are possible from this state (PossibleStates).
//
- void AddInsnClass(unsigned InsnClass, std::set<unsigned> &PossibleStates) const;
- //
+ void AddInsnClassStages(std::vector<unsigned> &InsnClass,
+ std::map<unsigned, unsigned> &ComboBitToBitsMap,
+ unsigned chkstage, unsigned numstages,
+ unsigned prevState, unsigned origState,
+ DenseSet<unsigned> &VisitedResourceStates,
+ std::set<unsigned> &PossibleStates) const;
+ //
// addTransition - Add a transition from this state given the input InsnClass
//
- void addTransition(unsigned InsnClass, const State *To) const;
+ void addTransition(std::vector<unsigned> InsnClass, const State *To) const;
//
// hasTransition - Returns true if there is a transition from this state
// given the input InsnClass
//
- bool hasTransition(unsigned InsnClass) const;
+ bool hasTransition(std::vector<unsigned> InsnClass) const;
};
} // End anonymous namespace.
@@ -144,10 +258,54 @@ public:
//
// writeTable: Print out a table representing the DFA.
//
- void writeTableAndAPI(raw_ostream &OS, const std::string &ClassName);
+ void writeTableAndAPI(raw_ostream &OS, const std::string &ClassName,
+ int numInsnClasses = 0,
+ int maxResources = 0, int numCombos = 0, int maxStages = 0);
};
} // End anonymous namespace.
+#ifndef NDEBUG
+// To enable debugging, run llvm-tblgen with: "-debug-only dfa-emitter".
+//
+// dbgsInsnClass - When debugging, print instruction class stages.
+//
+void dbgsInsnClass(const std::vector<unsigned> &InsnClass) {
+ DEBUG(dbgs() << "InsnClass: ");
+ for (unsigned i = 0; i < InsnClass.size(); ++i) {
+ if (i > 0) {
+ DEBUG(dbgs() << ", ");
+ }
+ DEBUG(dbgs() << "0x" << utohexstr(InsnClass[i]));
+ }
+ DFAInput InsnInput = getDFAInsnInput(InsnClass);
+ DEBUG(dbgs() << " (input: 0x" << utohexstr(InsnInput) << ")");
+}
+
+//
+// dbgsStateInfo - When debugging, print the set of state info.
+//
+void dbgsStateInfo(const std::set<unsigned> &stateInfo) {
+ DEBUG(dbgs() << "StateInfo: ");
+ unsigned i = 0;
+ for (std::set<unsigned>::iterator SI = stateInfo.begin();
+ SI != stateInfo.end(); ++SI, ++i) {
+ unsigned thisState = *SI;
+ if (i > 0) {
+ DEBUG(dbgs() << ", ");
+ }
+ DEBUG(dbgs() << "0x" << utohexstr(thisState));
+ }
+}
+
+//
+// dbgsIndent - When debugging, indent by the specified amount.
+//
+void dbgsIndent(unsigned indent) {
+ for (unsigned i = 0; i < indent; ++i) {
+ DEBUG(dbgs() << " ");
+ }
+}
+#endif
//
// Constructors and destructors for State and DFA
@@ -157,10 +315,11 @@ State::State() :
DFA::DFA(): currentState(nullptr) {}
-//
+//
// addTransition - Add a transition from this state given the input InsnClass
//
-void State::addTransition(unsigned InsnClass, const State *To) const {
+void State::addTransition(std::vector<unsigned> InsnClass, const State *To)
+ const {
assert(!Transitions.count(InsnClass) &&
"Cannot have multiple transitions for the same input");
Transitions[InsnClass] = To;
@@ -170,7 +329,7 @@ void State::addTransition(unsigned InsnClass, const State *To) const {
// hasTransition - Returns true if there is a transition from this state
// given the input InsnClass
//
-bool State::hasTransition(unsigned InsnClass) const {
+bool State::hasTransition(std::vector<unsigned> InsnClass) const {
return Transitions.count(InsnClass) > 0;
}
@@ -178,61 +337,170 @@ bool State::hasTransition(unsigned InsnClass) const {
// AddInsnClass - Return all combinations of resource reservation
// which are possible from this state (PossibleStates).
//
-void State::AddInsnClass(unsigned InsnClass,
- std::set<unsigned> &PossibleStates) const {
+// PossibleStates is the set of valid resource states that ensue from valid
+// transitions.
+//
+void State::AddInsnClass(std::vector<unsigned> &InsnClass,
+ std::map<unsigned, unsigned> &ComboBitToBitsMap,
+ std::set<unsigned> &PossibleStates) const {
//
// Iterate over all resource states in currentState.
//
+ unsigned numstages = InsnClass.size();
+ assert((numstages > 0) && "InsnClass has no stages");
for (std::set<unsigned>::iterator SI = stateInfo.begin();
SI != stateInfo.end(); ++SI) {
unsigned thisState = *SI;
- //
- // Iterate over all possible resources used in InsnClass.
- // For ex: for InsnClass = 0x11, all resources = {0x01, 0x10}.
- //
-
DenseSet<unsigned> VisitedResourceStates;
- for (unsigned int j = 0; j < sizeof(InsnClass) * 8; ++j) {
- if ((0x1 << j) & InsnClass) {
- //
- // For each possible resource used in InsnClass, generate the
- // resource state if that resource was used.
- //
- unsigned ResultingResourceState = thisState | (0x1 << j);
+
+ DEBUG(dbgs() << " thisState: 0x" << utohexstr(thisState) << "\n");
+ AddInsnClassStages(InsnClass, ComboBitToBitsMap,
+ numstages - 1, numstages,
+ thisState, thisState,
+ VisitedResourceStates, PossibleStates);
+ }
+}
+
+void State::AddInsnClassStages(std::vector<unsigned> &InsnClass,
+ std::map<unsigned, unsigned> &ComboBitToBitsMap,
+ unsigned chkstage, unsigned numstages,
+ unsigned prevState, unsigned origState,
+ DenseSet<unsigned> &VisitedResourceStates,
+ std::set<unsigned> &PossibleStates) const {
+
+ assert((chkstage < numstages) && "AddInsnClassStages: stage out of range");
+ unsigned thisStage = InsnClass[chkstage];
+
+ DEBUG({
+ dbgsIndent((1 + numstages - chkstage) << 1);
+ dbgs() << "AddInsnClassStages " << chkstage << " (0x"
+ << utohexstr(thisStage) << ") from ";
+ dbgsInsnClass(InsnClass);
+ dbgs() << "\n";
+ });
+
+ //
+ // Iterate over all possible resources used in thisStage.
+ // For ex: for thisStage = 0x11, all resources = {0x01, 0x10}.
+ //
+ for (unsigned int j = 0; j < DFA_MAX_RESOURCES; ++j) {
+ unsigned resourceMask = (0x1 << j);
+ if (resourceMask & thisStage) {
+ unsigned combo = ComboBitToBitsMap[resourceMask];
+ if (combo && ((~prevState & combo) != combo)) {
+ DEBUG(dbgs() << "\tSkipped Add 0x" << utohexstr(prevState)
+ << " - combo op 0x" << utohexstr(resourceMask)
+ << " (0x" << utohexstr(combo) <<") cannot be scheduled\n");
+ continue;
+ }
+ //
+ // For each possible resource used in thisStage, generate the
+ // resource state if that resource was used.
+ //
+ unsigned ResultingResourceState = prevState | resourceMask | combo;
+ DEBUG({
+ dbgsIndent((2 + numstages - chkstage) << 1);
+ dbgs() << "0x" << utohexstr(prevState)
+ << " | 0x" << utohexstr(resourceMask);
+ if (combo)
+ dbgs() << " | 0x" << utohexstr(combo);
+ dbgs() << " = 0x" << utohexstr(ResultingResourceState) << " ";
+ });
+
+ //
+ // If this is the final stage for this class
+ //
+ if (chkstage == 0) {
//
// Check if the resulting resource state can be accommodated in this
// packet.
- // We compute ResultingResourceState OR thisState.
- // If the result of the OR is different than thisState, it implies
+ // We compute resource OR prevState (originally started as origState).
+ // If the result of the OR is different than origState, it implies
// that there is at least one resource that can be used to schedule
- // InsnClass in the current packet.
+ // thisStage in the current packet.
// Insert ResultingResourceState into PossibleStates only if we haven't
// processed ResultingResourceState before.
//
- if ((ResultingResourceState != thisState) &&
- (VisitedResourceStates.count(ResultingResourceState) == 0)) {
- VisitedResourceStates.insert(ResultingResourceState);
- PossibleStates.insert(ResultingResourceState);
+ if (ResultingResourceState != prevState) {
+ if (VisitedResourceStates.count(ResultingResourceState) == 0) {
+ VisitedResourceStates.insert(ResultingResourceState);
+ PossibleStates.insert(ResultingResourceState);
+ DEBUG(dbgs() << "\tResultingResourceState: 0x"
+ << utohexstr(ResultingResourceState) << "\n");
+ } else {
+ DEBUG(dbgs() << "\tSkipped Add - state already seen\n");
+ }
+ } else {
+ DEBUG(dbgs() << "\tSkipped Add - no final resources available\n");
+ }
+ } else {
+ //
+ // If the current resource can be accommodated, check the next
+ // stage in InsnClass for available resources.
+ //
+ if (ResultingResourceState != prevState) {
+ DEBUG(dbgs() << "\n");
+ AddInsnClassStages(InsnClass, ComboBitToBitsMap,
+ chkstage - 1, numstages,
+ ResultingResourceState, origState,
+ VisitedResourceStates, PossibleStates);
+ } else {
+ DEBUG(dbgs() << "\tSkipped Add - no resources available\n");
}
}
}
}
-
}
//
-// canAddInsnClass - Quickly verifies if an instruction of type InsnClass is a
-// valid transition from this state i.e., can an instruction of type InsnClass
-// be added to the packet represented by this state.
+// canMaybeAddInsnClass - Quickly verifies if an instruction of type InsnClass
+// may be a valid transition from this state i.e., can an instruction of type
+// InsnClass be added to the packet represented by this state.
//
-bool State::canAddInsnClass(unsigned InsnClass) const {
+// Note that this routine is performing conservative checks that can be
+// quickly executed acting as a filter before calling AddInsnClassStages.
+// Any cases allowed through here will be caught later in AddInsnClassStages
+// which performs the more expensive exact check.
+//
+bool State::canMaybeAddInsnClass(std::vector<unsigned> &InsnClass,
+ std::map<unsigned, unsigned> &ComboBitToBitsMap) const {
for (std::set<unsigned>::const_iterator SI = stateInfo.begin();
SI != stateInfo.end(); ++SI) {
- if (~*SI & InsnClass)
+
+ // Check to see if all required resources are available.
+ bool available = true;
+
+ // Inspect each stage independently.
+ // note: This is a conservative check as we aren't checking for
+ // possible resource competition between the stages themselves
+ // The full cross product is examined later in AddInsnClass.
+ for (unsigned i = 0; i < InsnClass.size(); ++i) {
+ unsigned resources = *SI;
+ if ((~resources & InsnClass[i]) == 0) {
+ available = false;
+ break;
+ }
+ // Make sure _all_ resources for a combo function are available.
+ // note: This is a quick conservative check as it won't catch an
+ // unscheduleable combo if this stage is an OR expression
+ // containing a combo.
+ // These cases are caught later in AddInsnClass.
+ unsigned combo = ComboBitToBitsMap[InsnClass[i]];
+ if (combo && ((~resources & combo) != combo)) {
+ DEBUG(dbgs() << "\tSkipped canMaybeAdd 0x" << utohexstr(resources)
+ << " - combo op 0x" << utohexstr(InsnClass[i])
+ << " (0x" << utohexstr(combo) <<") cannot be scheduled\n");
+ available = false;
+ break;
+ }
+ }
+
+ if (available) {
return true;
+ }
}
return false;
}
@@ -244,7 +512,6 @@ const State &DFA::newState() {
return *IterPair.first;
}
-
int State::currentStateNum = 0;
DFAPacketizerEmitter::DFAPacketizerEmitter(RecordKeeper &R):
@@ -263,57 +530,100 @@ DFAPacketizerEmitter::DFAPacketizerEmitter(RecordKeeper &R):
// the ith state.
//
//
-void DFA::writeTableAndAPI(raw_ostream &OS, const std::string &TargetName) {
- static const std::string SentinelEntry = "{-1, -1}";
- DFA::StateSet::iterator SI = states.begin();
+void DFA::writeTableAndAPI(raw_ostream &OS, const std::string &TargetName,
+ int numInsnClasses,
+ int maxResources, int numCombos, int maxStages) {
+
+ unsigned numStates = states.size();
+
+ DEBUG(dbgs() << "-----------------------------------------------------------------------------\n");
+ DEBUG(dbgs() << "writeTableAndAPI\n");
+ DEBUG(dbgs() << "Total states: " << numStates << "\n");
+
+ OS << "namespace llvm {\n";
+
+ OS << "\n// Input format:\n";
+ OS << "#define DFA_MAX_RESTERMS " << DFA_MAX_RESTERMS
+ << "\t// maximum AND'ed resource terms\n";
+ OS << "#define DFA_MAX_RESOURCES " << DFA_MAX_RESOURCES
+ << "\t// maximum resource bits in one term\n";
+
+ OS << "\n// " << TargetName << "DFAStateInputTable[][2] = "
+ << "pairs of <Input, NextState> for all valid\n";
+ OS << "// transitions.\n";
+ OS << "// " << numStates << "\tstates\n";
+ OS << "// " << numInsnClasses << "\tinstruction classes\n";
+ OS << "// " << maxResources << "\tresources max\n";
+ OS << "// " << numCombos << "\tcombo resources\n";
+ OS << "// " << maxStages << "\tstages max\n";
+ OS << "const " << DFA_TBLTYPE << " "
+ << TargetName << "DFAStateInputTable[][2] = {\n";
+
// This table provides a map to the beginning of the transitions for State s
// in DFAStateInputTable.
- std::vector<int> StateEntry(states.size());
-
- OS << "namespace llvm {\n\n";
- OS << "const int " << TargetName << "DFAStateInputTable[][2] = {\n";
+ std::vector<int> StateEntry(numStates+1);
+ static const std::string SentinelEntry = "{-1, -1}";
// Tracks the total valid transitions encountered so far. It is used
// to construct the StateEntry table.
int ValidTransitions = 0;
- for (unsigned i = 0; i < states.size(); ++i, ++SI) {
+ DFA::StateSet::iterator SI = states.begin();
+ for (unsigned i = 0; i < numStates; ++i, ++SI) {
assert ((SI->stateNum == (int) i) && "Mismatch in state numbers");
StateEntry[i] = ValidTransitions;
for (State::TransitionMap::iterator
II = SI->Transitions.begin(), IE = SI->Transitions.end();
II != IE; ++II) {
- OS << "{" << II->first << ", "
+ OS << "{0x" << utohexstr(getDFAInsnInput(II->first)) << ", "
<< II->second->stateNum
- << "}, ";
+ << "},\t";
}
ValidTransitions += SI->Transitions.size();
// If there are no valid transitions from this stage, we need a sentinel
// transition.
if (ValidTransitions == StateEntry[i]) {
- OS << SentinelEntry << ",";
+ OS << SentinelEntry << ",\t";
++ValidTransitions;
}
+ OS << " // state " << i << ": " << StateEntry[i];
+ if (StateEntry[i] != (ValidTransitions-1)) { // More than one transition.
+ OS << "-" << (ValidTransitions-1);
+ }
OS << "\n";
}
// Print out a sentinel entry at the end of the StateInputTable. This is
// needed to iterate over StateInputTable in DFAPacketizer::ReadTable()
- OS << SentinelEntry << "\n";
-
+ OS << SentinelEntry << "\t";
+ OS << " // state " << numStates << ": " << ValidTransitions;
+ OS << "\n";
+
OS << "};\n\n";
+ OS << "// " << TargetName << "DFAStateEntryTable[i] = "
+ << "Index of the first entry in DFAStateInputTable for\n";
+ OS << "// "
+ << "the ith state.\n";
+ OS << "// " << numStates << " states\n";
OS << "const unsigned int " << TargetName << "DFAStateEntryTable[] = {\n";
// Multiply i by 2 since each entry in DFAStateInputTable is a set of
// two numbers.
- for (unsigned i = 0; i < states.size(); ++i)
+ unsigned lastState = 0;
+ for (unsigned i = 0; i < numStates; ++i) {
+ if (i && ((i % 10) == 0)) {
+ lastState = i-1;
+ OS << " // states " << (i-10) << ":" << lastState << "\n";
+ }
OS << StateEntry[i] << ", ";
+ }
// Print out the index to the sentinel entry in StateInputTable
OS << ValidTransitions << ", ";
+ OS << " // states " << (lastState+1) << ":" << numStates << "\n";
- OS << "\n};\n";
+ OS << "};\n";
OS << "} // namespace\n";
@@ -332,40 +642,118 @@ void DFA::writeTableAndAPI(raw_ostream &OS, const std::string &TargetName) {
//
-// collectAllInsnClasses - Populate allInsnClasses which is a set of units
-// used in each stage.
+// collectAllFuncUnits - Construct a map of function unit names to bits.
//
-void DFAPacketizerEmitter::collectAllInsnClasses(const std::string &Name,
- Record *ItinData,
- unsigned &NStages,
- raw_ostream &OS) {
- // Collect processor itineraries.
- std::vector<Record*> ProcItinList =
- Records.getAllDerivedDefinitions("ProcessorItineraries");
-
- // If just no itinerary then don't bother.
- if (ProcItinList.size() < 2)
- return;
- std::map<std::string, unsigned> NameToBitsMap;
+int DFAPacketizerEmitter::collectAllFuncUnits(
+ std::vector<Record*> &ProcItinList,
+ std::map<std::string, unsigned> &FUNameToBitsMap,
+ int &maxFUs,
+ raw_ostream &OS) {
+ DEBUG(dbgs() << "-----------------------------------------------------------------------------\n");
+ DEBUG(dbgs() << "collectAllFuncUnits");
+ DEBUG(dbgs() << " (" << ProcItinList.size() << " itineraries)\n");
+ int totalFUs = 0;
// Parse functional units for all the itineraries.
for (unsigned i = 0, N = ProcItinList.size(); i < N; ++i) {
Record *Proc = ProcItinList[i];
std::vector<Record*> FUs = Proc->getValueAsListOfDefs("FU");
+ DEBUG(dbgs() << " FU:" << i
+ << " (" << FUs.size() << " FUs) "
+ << Proc->getName());
+
+
// Convert macros to bits for each stage.
- for (unsigned i = 0, N = FUs.size(); i < N; ++i)
- NameToBitsMap[FUs[i]->getName()] = (unsigned) (1U << i);
+ unsigned numFUs = FUs.size();
+ for (unsigned j = 0; j < numFUs; ++j) {
+ assert ((j < DFA_MAX_RESOURCES) &&
+ "Exceeded maximum number of representable resources");
+ unsigned FuncResources = (unsigned) (1U << j);
+ FUNameToBitsMap[FUs[j]->getName()] = FuncResources;
+ DEBUG(dbgs() << " " << FUs[j]->getName()
+ << ":0x" << utohexstr(FuncResources));
+ }
+ if (((int) numFUs) > maxFUs) {
+ maxFUs = numFUs;
+ }
+ totalFUs += numFUs;
+ DEBUG(dbgs() << "\n");
}
+ return totalFUs;
+}
+
+//
+// collectAllComboFuncs - Construct a map from a combo function unit bit to
+// the bits of all included functional units.
+//
+int DFAPacketizerEmitter::collectAllComboFuncs(
+ std::vector<Record*> &ComboFuncList,
+ std::map<std::string, unsigned> &FUNameToBitsMap,
+ std::map<unsigned, unsigned> &ComboBitToBitsMap,
+ raw_ostream &OS) {
+ DEBUG(dbgs() << "-----------------------------------------------------------------------------\n");
+ DEBUG(dbgs() << "collectAllComboFuncs");
+ DEBUG(dbgs() << " (" << ComboFuncList.size() << " sets)\n");
+
+ int numCombos = 0;
+ for (unsigned i = 0, N = ComboFuncList.size(); i < N; ++i) {
+ Record *Func = ComboFuncList[i];
+ std::vector<Record*> FUs = Func->getValueAsListOfDefs("CFD");
+ DEBUG(dbgs() << " CFD:" << i
+ << " (" << FUs.size() << " combo FUs) "
+ << Func->getName() << "\n");
+
+ // Convert macros to bits for each stage.
+ for (unsigned j = 0, N = FUs.size(); j < N; ++j) {
+ assert ((j < DFA_MAX_RESOURCES) &&
+ "Exceeded maximum number of DFA resources");
+ Record *FuncData = FUs[j];
+ Record *ComboFunc = FuncData->getValueAsDef("TheComboFunc");
+ const std::vector<Record*> &FuncList =
+ FuncData->getValueAsListOfDefs("FuncList");
+ std::string ComboFuncName = ComboFunc->getName();
+ unsigned ComboBit = FUNameToBitsMap[ComboFuncName];
+ unsigned ComboResources = ComboBit;
+ DEBUG(dbgs() << " combo: " << ComboFuncName
+ << ":0x" << utohexstr(ComboResources) << "\n");
+ for (unsigned k = 0, M = FuncList.size(); k < M; ++k) {
+ std::string FuncName = FuncList[k]->getName();
+ unsigned FuncResources = FUNameToBitsMap[FuncName];
+ DEBUG(dbgs() << " " << FuncName
+ << ":0x" << utohexstr(FuncResources) << "\n");
+ ComboResources |= FuncResources;
+ }
+ ComboBitToBitsMap[ComboBit] = ComboResources;
+ numCombos++;
+ DEBUG(dbgs() << " => combo bits: " << ComboFuncName << ":0x"
+ << utohexstr(ComboBit) << " = 0x"
+ << utohexstr(ComboResources) << "\n");
+ }
+ }
+ return numCombos;
+}
+
+
+//
+// collectOneInsnClass - Populate allInsnClasses with one instruction class
+//
+int DFAPacketizerEmitter::collectOneInsnClass(const std::string &ProcName,
+ std::vector<Record*> &ProcItinList,
+ std::map<std::string, unsigned> &FUNameToBitsMap,
+ Record *ItinData,
+ raw_ostream &OS) {
const std::vector<Record*> &StageList =
ItinData->getValueAsListOfDefs("Stages");
// The number of stages.
- NStages = StageList.size();
+ unsigned NStages = StageList.size();
- // For each unit.
- unsigned UnitBitValue = 0;
+ DEBUG(dbgs() << " " << ItinData->getValueAsDef("TheClass")->getName()
+ << "\n");
+
+ std::vector<unsigned> UnitBits;
// Compute the bitwise or of each unit used in this stage.
for (unsigned i = 0; i < NStages; ++i) {
@@ -375,18 +763,74 @@ void DFAPacketizerEmitter::collectAllInsnClasses(const std::string &Name,
const std::vector<Record*> &UnitList =
Stage->getValueAsListOfDefs("Units");
+ DEBUG(dbgs() << " stage:" << i
+ << " [" << UnitList.size() << " units]:");
+ unsigned dbglen = 26; // cursor after stage dbgs
+
+ // Compute the bitwise or of each unit used in this stage.
+ unsigned UnitBitValue = 0;
for (unsigned j = 0, M = UnitList.size(); j < M; ++j) {
// Conduct bitwise or.
std::string UnitName = UnitList[j]->getName();
- assert(NameToBitsMap.count(UnitName));
- UnitBitValue |= NameToBitsMap[UnitName];
+ DEBUG(dbgs() << " " << j << ":" << UnitName);
+ dbglen += 3 + UnitName.length();
+ assert(FUNameToBitsMap.count(UnitName));
+ UnitBitValue |= FUNameToBitsMap[UnitName];
}
if (UnitBitValue != 0)
- allInsnClasses.insert(UnitBitValue);
+ UnitBits.push_back(UnitBitValue);
+
+ while (dbglen <= 64) { // line up bits dbgs
+ dbglen += 8;
+ DEBUG(dbgs() << "\t");
+ }
+ DEBUG(dbgs() << " (bits: 0x" << utohexstr(UnitBitValue) << ")\n");
}
+
+ if (UnitBits.size() > 0)
+ allInsnClasses.push_back(UnitBits);
+
+ DEBUG({
+ dbgs() << " ";
+ dbgsInsnClass(UnitBits);
+ dbgs() << "\n";
+ });
+
+ return NStages;
}
+//
+// collectAllInsnClasses - Populate allInsnClasses which is a set of units
+// used in each stage.
+//
+int DFAPacketizerEmitter::collectAllInsnClasses(const std::string &ProcName,
+ std::vector<Record*> &ProcItinList,
+ std::map<std::string, unsigned> &FUNameToBitsMap,
+ std::vector<Record*> &ItinDataList,
+ int &maxStages,
+ raw_ostream &OS) {
+ // Collect all instruction classes.
+ unsigned M = ItinDataList.size();
+
+ int numInsnClasses = 0;
+ DEBUG(dbgs() << "-----------------------------------------------------------------------------\n"
+ << "collectAllInsnClasses "
+ << ProcName
+ << " (" << M << " classes)\n");
+
+ // Collect stages for each instruction class for all itinerary data
+ for (unsigned j = 0; j < M; j++) {
+ Record *ItinData = ItinDataList[j];
+ int NStages = collectOneInsnClass(ProcName, ProcItinList,
+ FUNameToBitsMap, ItinData, OS);
+ if (NStages > maxStages) {
+ maxStages = NStages;
+ }
+ numInsnClasses++;
+ }
+ return numInsnClasses;
+}
//
// Run the worklist algorithm to generate the DFA.
@@ -398,16 +842,35 @@ void DFAPacketizerEmitter::run(raw_ostream &OS) {
Records.getAllDerivedDefinitions("ProcessorItineraries");
//
- // Collect the instruction classes.
+ // Collect the Functional units.
+ //
+ std::map<std::string, unsigned> FUNameToBitsMap;
+ int maxResources = 0;
+ collectAllFuncUnits(ProcItinList,
+ FUNameToBitsMap, maxResources, OS);
+
+ //
+ // Collect the Combo Functional units.
//
+ std::map<unsigned, unsigned> ComboBitToBitsMap;
+ std::vector<Record*> ComboFuncList =
+ Records.getAllDerivedDefinitions("ComboFuncUnits");
+ int numCombos = collectAllComboFuncs(ComboFuncList,
+ FUNameToBitsMap, ComboBitToBitsMap, OS);
+
+ //
+ // Collect the itineraries.
+ //
+ int maxStages = 0;
+ int numInsnClasses = 0;
for (unsigned i = 0, N = ProcItinList.size(); i < N; i++) {
Record *Proc = ProcItinList[i];
// Get processor itinerary name.
- const std::string &Name = Proc->getName();
+ const std::string &ProcName = Proc->getName();
// Skip default.
- if (Name == "NoItineraries")
+ if (ProcName == "NoItineraries")
continue;
// Sanity check for at least one instruction itinerary class.
@@ -419,15 +882,11 @@ void DFAPacketizerEmitter::run(raw_ostream &OS) {
// Get itinerary data list.
std::vector<Record*> ItinDataList = Proc->getValueAsListOfDefs("IID");
- // Collect instruction classes for all itinerary data.
- for (unsigned j = 0, M = ItinDataList.size(); j < M; j++) {
- Record *ItinData = ItinDataList[j];
- unsigned NStages;
- collectAllInsnClasses(Name, ItinData, NStages, OS);
- }
+ // Collect all instruction classes
+ numInsnClasses += collectAllInsnClasses(ProcName, ProcItinList,
+ FUNameToBitsMap, ItinDataList, maxStages, OS);
}
-
//
// Run a worklist algorithm to generate the DFA.
//
@@ -436,6 +895,7 @@ void DFAPacketizerEmitter::run(raw_ostream &OS) {
Initial->isInitial = true;
Initial->stateInfo.insert(0x0);
SmallVector<const State*, 32> WorkList;
+// std::queue<State*> WorkList;
std::map<std::set<unsigned>, const State*> Visited;
WorkList.push_back(Initial);
@@ -459,9 +919,19 @@ void DFAPacketizerEmitter::run(raw_ostream &OS) {
//
while (!WorkList.empty()) {
const State *current = WorkList.pop_back_val();
- for (DenseSet<unsigned>::iterator CI = allInsnClasses.begin(),
- CE = allInsnClasses.end(); CI != CE; ++CI) {
- unsigned InsnClass = *CI;
+ DEBUG({
+ dbgs() << "---------------------\n";
+ dbgs() << "Processing state: " << current->stateNum << " - ";
+ dbgsStateInfo(current->stateInfo);
+ dbgs() << "\n";
+ });
+ for (unsigned i = 0; i < allInsnClasses.size(); i++) {
+ std::vector<unsigned> InsnClass = allInsnClasses[i];
+ DEBUG({
+ dbgs() << i << " ";
+ dbgsInsnClass(InsnClass);
+ dbgs() << "\n";
+ });
std::set<unsigned> NewStateResources;
//
@@ -469,32 +939,52 @@ void DFAPacketizerEmitter::run(raw_ostream &OS) {
// and the state can accommodate this InsnClass, create a transition.
//
if (!current->hasTransition(InsnClass) &&
- current->canAddInsnClass(InsnClass)) {
- const State *NewState;
- current->AddInsnClass(InsnClass, NewStateResources);
- assert(!NewStateResources.empty() && "New states must be generated");
+ current->canMaybeAddInsnClass(InsnClass, ComboBitToBitsMap)) {
+ const State *NewState = NULL;
+ current->AddInsnClass(InsnClass, ComboBitToBitsMap, NewStateResources);
+ if (NewStateResources.size() == 0) {
+ DEBUG(dbgs() << " Skipped - no new states generated\n");
+ continue;
+ }
+
+ DEBUG({
+ dbgs() << "\t";
+ dbgsStateInfo(NewStateResources);
+ dbgs() << "\n";
+ });
//
// If we have seen this state before, then do not create a new state.
//
- //
auto VI = Visited.find(NewStateResources);
- if (VI != Visited.end())
+ if (VI != Visited.end()) {
NewState = VI->second;
- else {
+ DEBUG({
+ dbgs() << "\tFound existing state: " << NewState->stateNum
+ << " - ";
+ dbgsStateInfo(NewState->stateInfo);
+ dbgs() << "\n";
+ });
+ } else {
NewState = &D.newState();
NewState->stateInfo = NewStateResources;
Visited[NewStateResources] = NewState;
WorkList.push_back(NewState);
+ DEBUG({
+ dbgs() << "\tAccepted new state: " << NewState->stateNum << " - ";
+ dbgsStateInfo(NewState->stateInfo);
+ dbgs() << "\n";
+ });
}
-
+
current->addTransition(InsnClass, NewState);
}
}
}
// Print out the table.
- D.writeTableAndAPI(OS, TargetName);
+ D.writeTableAndAPI(OS, TargetName,
+ numInsnClasses, maxResources, numCombos, maxStages);
}
namespace llvm {
diff --git a/utils/TableGen/DisassemblerEmitter.cpp b/utils/TableGen/DisassemblerEmitter.cpp
index f02051a2cd6c7..e8595271bccef 100644
--- a/utils/TableGen/DisassemblerEmitter.cpp
+++ b/utils/TableGen/DisassemblerEmitter.cpp
@@ -134,7 +134,7 @@ void EmitDisassembler(RecordKeeper &Records, raw_ostream &OS) {
PredicateNamespace = "ARM";
EmitFixedLenDecoder(Records, OS, PredicateNamespace,
- "if (!Check(S, ", ")) return MCDisassembler::Fail;",
+ "if (!Check(S, ", "))",
"S", "MCDisassembler::Fail",
" MCDisassembler::DecodeStatus S = "
"MCDisassembler::Success;\n(void)S;");
@@ -142,8 +142,7 @@ void EmitDisassembler(RecordKeeper &Records, raw_ostream &OS) {
}
EmitFixedLenDecoder(Records, OS, Target.getName(),
- "if (", " == MCDisassembler::Fail)"
- " return MCDisassembler::Fail;",
+ "if (", " == MCDisassembler::Fail)",
"MCDisassembler::Success", "MCDisassembler::Fail", "");
}
diff --git a/utils/TableGen/FixedLenDecoderEmitter.cpp b/utils/TableGen/FixedLenDecoderEmitter.cpp
index c4df2833885a5..8ca4a1bf54040 100644
--- a/utils/TableGen/FixedLenDecoderEmitter.cpp
+++ b/utils/TableGen/FixedLenDecoderEmitter.cpp
@@ -44,9 +44,10 @@ struct EncodingField {
struct OperandInfo {
std::vector<EncodingField> Fields;
std::string Decoder;
+ bool HasCompleteDecoder;
- OperandInfo(std::string D)
- : Decoder(D) { }
+ OperandInfo(std::string D, bool HCD)
+ : Decoder(D), HasCompleteDecoder(HCD) { }
void addField(unsigned Base, unsigned Width, unsigned Offset) {
Fields.push_back(EncodingField(Base, Width, Offset));
@@ -64,8 +65,8 @@ typedef std::vector<uint8_t> DecoderTable;
typedef uint32_t DecoderFixup;
typedef std::vector<DecoderFixup> FixupList;
typedef std::vector<FixupList> FixupScopeList;
-typedef SetVector<std::string> PredicateSet;
-typedef SetVector<std::string> DecoderSet;
+typedef SmallSetVector<std::string, 16> PredicateSet;
+typedef SmallSetVector<std::string, 16> DecoderSet;
struct DecoderTableInfo {
DecoderTable Table;
FixupScopeList FixupStack;
@@ -85,8 +86,7 @@ public:
FixedLenDecoderEmitter(RecordKeeper &R,
std::string PredicateNamespace,
std::string GPrefix = "if (",
- std::string GPostfix = " == MCDisassembler::Fail)"
- " return MCDisassembler::Fail;",
+ std::string GPostfix = " == MCDisassembler::Fail)",
std::string ROK = "MCDisassembler::Success",
std::string RFail = "MCDisassembler::Fail",
std::string L = "") :
@@ -448,10 +448,13 @@ protected:
const Filter &Best) const;
void emitBinaryParser(raw_ostream &o, unsigned &Indentation,
- const OperandInfo &OpInfo) const;
+ const OperandInfo &OpInfo,
+ bool &OpHasCompleteDecoder) const;
- void emitDecoder(raw_ostream &OS, unsigned Indentation, unsigned Opc) const;
- unsigned getDecoderIndex(DecoderSet &Decoders, unsigned Opc) const;
+ void emitDecoder(raw_ostream &OS, unsigned Indentation, unsigned Opc,
+ bool &HasCompleteDecoder) const;
+ unsigned getDecoderIndex(DecoderSet &Decoders, unsigned Opc,
+ bool &HasCompleteDecoder) const;
// Assign a single filter and run with it.
void runSingleFilter(unsigned startBit, unsigned numBit, bool mixed);
@@ -779,7 +782,9 @@ void FixedLenDecoderEmitter::emitTable(formatted_raw_ostream &OS,
OS << "// Skip to: " << ((I - Table.begin()) + NumToSkip) << "\n";
break;
}
- case MCD::OPC_Decode: {
+ case MCD::OPC_Decode:
+ case MCD::OPC_TryDecode: {
+ bool IsTry = *I == MCD::OPC_TryDecode;
++I;
// Extract the ULEB128 encoded Opcode to a buffer.
uint8_t Buffer[8], *p = Buffer;
@@ -788,7 +793,8 @@ void FixedLenDecoderEmitter::emitTable(formatted_raw_ostream &OS,
&& "ULEB128 value too large!");
// Decode the Opcode value.
unsigned Opc = decodeULEB128(Buffer);
- OS.indent(Indentation) << "MCD::OPC_Decode, ";
+ OS.indent(Indentation) << "MCD::OPC_" << (IsTry ? "Try" : "")
+ << "Decode, ";
for (p = Buffer; *p >= 128; ++p)
OS << utostr(*p) << ", ";
OS << utostr(*p) << ", ";
@@ -798,8 +804,25 @@ void FixedLenDecoderEmitter::emitTable(formatted_raw_ostream &OS,
OS << utostr(*I) << ", ";
OS << utostr(*I++) << ", ";
+ if (!IsTry) {
+ OS << "// Opcode: "
+ << NumberedInstructions->at(Opc)->TheDef->getName() << "\n";
+ break;
+ }
+
+ // Fallthrough for OPC_TryDecode.
+
+ // 16-bit numtoskip value.
+ uint8_t Byte = *I++;
+ uint32_t NumToSkip = Byte;
+ OS << utostr(Byte) << ", ";
+ Byte = *I++;
+ OS << utostr(Byte) << ", ";
+ NumToSkip |= Byte << 8;
+
OS << "// Opcode: "
- << NumberedInstructions->at(Opc)->TheDef->getName() << "\n";
+ << NumberedInstructions->at(Opc)->TheDef->getName()
+ << ", skip to: " << ((I - Table.begin()) + NumToSkip) << "\n";
break;
}
case MCD::OPC_SoftFail: {
@@ -876,8 +899,9 @@ emitDecoderFunction(formatted_raw_ostream &OS, DecoderSet &Decoders,
OS.indent(Indentation) << "static DecodeStatus decodeToMCInst(DecodeStatus S,"
<< " unsigned Idx, InsnType insn, MCInst &MI,\n";
OS.indent(Indentation) << " uint64_t "
- << "Address, const void *Decoder) {\n";
+ << "Address, const void *Decoder, bool &DecodeComplete) {\n";
Indentation += 2;
+ OS.indent(Indentation) << "DecodeComplete = true;\n";
OS.indent(Indentation) << "InsnType tmp;\n";
OS.indent(Indentation) << "switch (Idx) {\n";
OS.indent(Indentation) << "default: llvm_unreachable(\"Invalid index!\");\n";
@@ -1033,7 +1057,8 @@ unsigned FilterChooser::getIslands(std::vector<unsigned> &StartBits,
}
void FilterChooser::emitBinaryParser(raw_ostream &o, unsigned &Indentation,
- const OperandInfo &OpInfo) const {
+ const OperandInfo &OpInfo,
+ bool &OpHasCompleteDecoder) const {
const std::string &Decoder = OpInfo.Decoder;
if (OpInfo.numFields() != 1)
@@ -1049,45 +1074,57 @@ void FilterChooser::emitBinaryParser(raw_ostream &o, unsigned &Indentation,
o << ";\n";
}
- if (Decoder != "")
+ if (Decoder != "") {
+ OpHasCompleteDecoder = OpInfo.HasCompleteDecoder;
o.indent(Indentation) << Emitter->GuardPrefix << Decoder
- << "(MI, tmp, Address, Decoder)"
- << Emitter->GuardPostfix << "\n";
- else
+ << "(MI, tmp, Address, Decoder)"
+ << Emitter->GuardPostfix
+ << " { " << (OpHasCompleteDecoder ? "" : "DecodeComplete = false; ")
+ << "return MCDisassembler::Fail; }\n";
+ } else {
+ OpHasCompleteDecoder = true;
o.indent(Indentation) << "MI.addOperand(MCOperand::createImm(tmp));\n";
-
+ }
}
void FilterChooser::emitDecoder(raw_ostream &OS, unsigned Indentation,
- unsigned Opc) const {
+ unsigned Opc, bool &HasCompleteDecoder) const {
+ HasCompleteDecoder = true;
+
for (const auto &Op : Operands.find(Opc)->second) {
// If a custom instruction decoder was specified, use that.
if (Op.numFields() == 0 && Op.Decoder.size()) {
+ HasCompleteDecoder = Op.HasCompleteDecoder;
OS.indent(Indentation) << Emitter->GuardPrefix << Op.Decoder
<< "(MI, insn, Address, Decoder)"
- << Emitter->GuardPostfix << "\n";
+ << Emitter->GuardPostfix
+ << " { " << (HasCompleteDecoder ? "" : "DecodeComplete = false; ")
+ << "return MCDisassembler::Fail; }\n";
break;
}
- emitBinaryParser(OS, Indentation, Op);
+ bool OpHasCompleteDecoder;
+ emitBinaryParser(OS, Indentation, Op, OpHasCompleteDecoder);
+ if (!OpHasCompleteDecoder)
+ HasCompleteDecoder = false;
}
}
unsigned FilterChooser::getDecoderIndex(DecoderSet &Decoders,
- unsigned Opc) const {
+ unsigned Opc,
+ bool &HasCompleteDecoder) const {
// Build up the predicate string.
SmallString<256> Decoder;
// FIXME: emitDecoder() function can take a buffer directly rather than
// a stream.
raw_svector_ostream S(Decoder);
unsigned I = 4;
- emitDecoder(S, I, Opc);
- S.flush();
+ emitDecoder(S, I, Opc, HasCompleteDecoder);
// Using the full decoder string as the key value here is a bit
// heavyweight, but is effective. If the string comparisons become a
// performance concern, we can implement a mangling of the predicate
- // data easilly enough with a map back to the actual string. That's
+ // data easily enough with a map back to the actual string. That's
// overkill for now, though.
// Make sure the predicate is in the table.
@@ -1162,7 +1199,7 @@ unsigned FilterChooser::getPredicateIndex(DecoderTableInfo &TableInfo,
// Using the full predicate string as the key value here is a bit
// heavyweight, but is effective. If the string comparisons become a
// performance concern, we can implement a mangling of the predicate
- // data easilly enough with a map back to the actual string. That's
+ // data easily enough with a map back to the actual string. That's
// overkill for now, though.
// Make sure the predicate is in the table.
@@ -1193,7 +1230,6 @@ void FilterChooser::emitPredicateTableEntry(DecoderTableInfo &TableInfo,
SmallString<16> PBytes;
raw_svector_ostream S(PBytes);
encodeULEB128(PIdx, S);
- S.flush();
TableInfo.Table.push_back(MCD::OPC_CheckPredicate);
// Predicate index
@@ -1252,16 +1288,13 @@ void FilterChooser::emitSoftFailTableEntry(DecoderTableInfo &TableInfo,
raw_svector_ostream S(MaskBytes);
if (NeedPositiveMask) {
encodeULEB128(PositiveMask.getZExtValue(), S);
- S.flush();
for (unsigned i = 0, e = MaskBytes.size(); i != e; ++i)
TableInfo.Table.push_back(MaskBytes[i]);
} else
TableInfo.Table.push_back(0);
if (NeedNegativeMask) {
MaskBytes.clear();
- S.resync();
encodeULEB128(NegativeMask.getZExtValue(), S);
- S.flush();
for (unsigned i = 0, e = MaskBytes.size(); i != e; ++i)
TableInfo.Table.push_back(MaskBytes[i]);
} else
@@ -1308,22 +1341,41 @@ void FilterChooser::emitSingletonTableEntry(DecoderTableInfo &TableInfo,
// Check for soft failure of the match.
emitSoftFailTableEntry(TableInfo, Opc);
- TableInfo.Table.push_back(MCD::OPC_Decode);
+ bool HasCompleteDecoder;
+ unsigned DIdx = getDecoderIndex(TableInfo.Decoders, Opc, HasCompleteDecoder);
+
+ // Produce OPC_Decode or OPC_TryDecode opcode based on the information
+ // whether the instruction decoder is complete or not. If it is complete
+ // then it handles all possible values of remaining variable/unfiltered bits
+ // and for any value can determine if the bitpattern is a valid instruction
+ // or not. This means OPC_Decode will be the final step in the decoding
+ // process. If it is not complete, then the Fail return code from the
+ // decoder method indicates that additional processing should be done to see
+ // if there is any other instruction that also matches the bitpattern and
+ // can decode it.
+ TableInfo.Table.push_back(HasCompleteDecoder ? MCD::OPC_Decode :
+ MCD::OPC_TryDecode);
uint8_t Buffer[8], *p;
encodeULEB128(Opc, Buffer);
for (p = Buffer; *p >= 128 ; ++p)
TableInfo.Table.push_back(*p);
TableInfo.Table.push_back(*p);
- unsigned DIdx = getDecoderIndex(TableInfo.Decoders, Opc);
SmallString<16> Bytes;
raw_svector_ostream S(Bytes);
encodeULEB128(DIdx, S);
- S.flush();
// Decoder index
for (unsigned i = 0, e = Bytes.size(); i != e; ++i)
TableInfo.Table.push_back(Bytes[i]);
+
+ if (!HasCompleteDecoder) {
+ // Push location for NumToSkip backpatching.
+ TableInfo.FixupStack.back().push_back(TableInfo.Table.size());
+ // Allocate the space for the fixup.
+ TableInfo.Table.push_back(0);
+ TableInfo.Table.push_back(0);
+ }
}
// Emits table entries to decode the singleton, and then to decode the rest.
@@ -1679,7 +1731,8 @@ static bool populateInstruction(CodeGenTarget &Target,
// of trying to auto-generate the decoder.
std::string InstDecoder = Def.getValueAsString("DecoderMethod");
if (InstDecoder != "") {
- InsnOperands.push_back(OperandInfo(InstDecoder));
+ bool HasCompleteInstDecoder = Def.getValueAsBit("hasCompleteDecoder");
+ InsnOperands.push_back(OperandInfo(InstDecoder, HasCompleteInstDecoder));
Operands[Opc] = InsnOperands;
return true;
}
@@ -1835,7 +1888,14 @@ static bool populateInstruction(CodeGenTarget &Target,
if (!isReg && String && String->getValue() != "")
Decoder = String->getValue();
- OperandInfo OpInfo(Decoder);
+ RecordVal *HasCompleteDecoderVal =
+ TypeRecord->getValue("hasCompleteDecoder");
+ BitInit *HasCompleteDecoderBit = HasCompleteDecoderVal ?
+ dyn_cast<BitInit>(HasCompleteDecoderVal->getValue()) : nullptr;
+ bool HasCompleteDecoder = HasCompleteDecoderBit ?
+ HasCompleteDecoderBit->getValue() : true;
+
+ OperandInfo OpInfo(Decoder, HasCompleteDecoder);
OpInfo.addField(bitStart, bitWidth, 0);
NumberedInsnOperands[Name].push_back(OpInfo);
@@ -1907,7 +1967,14 @@ static bool populateInstruction(CodeGenTarget &Target,
if (!isReg && String && String->getValue() != "")
Decoder = String->getValue();
- OperandInfo OpInfo(Decoder);
+ RecordVal *HasCompleteDecoderVal =
+ TypeRecord->getValue("hasCompleteDecoder");
+ BitInit *HasCompleteDecoderBit = HasCompleteDecoderVal ?
+ dyn_cast<BitInit>(HasCompleteDecoderVal->getValue()) : nullptr;
+ bool HasCompleteDecoder = HasCompleteDecoderBit ?
+ HasCompleteDecoderBit->getValue() : true;
+
+ OperandInfo OpInfo(Decoder, HasCompleteDecoder);
unsigned Base = ~0U;
unsigned Width = 0;
unsigned Offset = 0;
@@ -2096,12 +2163,52 @@ static void emitDecodeInstruction(formatted_raw_ostream &OS) {
<< " Ptr += Len;\n"
<< " unsigned DecodeIdx = decodeULEB128(Ptr, &Len);\n"
<< " Ptr += Len;\n"
- << " DEBUG(dbgs() << Loc << \": OPC_Decode: opcode \" << Opc\n"
- << " << \", using decoder \" << DecodeIdx << \"\\n\" );\n"
- << " DEBUG(dbgs() << \"----- DECODE SUCCESSFUL -----\\n\");\n"
<< "\n"
+ << " MI.clear();\n"
<< " MI.setOpcode(Opc);\n"
- << " return decodeToMCInst(S, DecodeIdx, insn, MI, Address, DisAsm);\n"
+ << " bool DecodeComplete;\n"
+ << " S = decodeToMCInst(S, DecodeIdx, insn, MI, Address, DisAsm, DecodeComplete);\n"
+ << " assert(DecodeComplete);\n"
+ << "\n"
+ << " DEBUG(dbgs() << Loc << \": OPC_Decode: opcode \" << Opc\n"
+ << " << \", using decoder \" << DecodeIdx << \": \"\n"
+ << " << (S != MCDisassembler::Fail ? \"PASS\" : \"FAIL\") << \"\\n\");\n"
+ << " return S;\n"
+ << " }\n"
+ << " case MCD::OPC_TryDecode: {\n"
+ << " unsigned Len;\n"
+ << " // Decode the Opcode value.\n"
+ << " unsigned Opc = decodeULEB128(++Ptr, &Len);\n"
+ << " Ptr += Len;\n"
+ << " unsigned DecodeIdx = decodeULEB128(Ptr, &Len);\n"
+ << " Ptr += Len;\n"
+ << " // NumToSkip is a plain 16-bit integer.\n"
+ << " unsigned NumToSkip = *Ptr++;\n"
+ << " NumToSkip |= (*Ptr++) << 8;\n"
+ << "\n"
+ << " // Perform the decode operation.\n"
+ << " MCInst TmpMI;\n"
+ << " TmpMI.setOpcode(Opc);\n"
+ << " bool DecodeComplete;\n"
+ << " S = decodeToMCInst(S, DecodeIdx, insn, TmpMI, Address, DisAsm, DecodeComplete);\n"
+ << " DEBUG(dbgs() << Loc << \": OPC_TryDecode: opcode \" << Opc\n"
+ << " << \", using decoder \" << DecodeIdx << \": \");\n"
+ << "\n"
+ << " if (DecodeComplete) {\n"
+ << " // Decoding complete.\n"
+ << " DEBUG(dbgs() << (S != MCDisassembler::Fail ? \"PASS\" : \"FAIL\") << \"\\n\");\n"
+ << " MI = TmpMI;\n"
+ << " return S;\n"
+ << " } else {\n"
+ << " assert(S == MCDisassembler::Fail);\n"
+ << " // If the decoding was incomplete, skip.\n"
+ << " Ptr += NumToSkip;\n"
+ << " DEBUG(dbgs() << \"FAIL: continuing at \" << (Ptr - DecodeTable) << \"\\n\");\n"
+ << " // Reset decode status. This also drops a SoftFail status that could be\n"
+ << " // set before the decode attempt.\n"
+ << " S = MCDisassembler::Success;\n"
+ << " }\n"
+ << " break;\n"
<< " }\n"
<< " case MCD::OPC_SoftFail: {\n"
<< " // Decode the mask values.\n"
diff --git a/utils/TableGen/InstrInfoEmitter.cpp b/utils/TableGen/InstrInfoEmitter.cpp
index e242a965ff93c..a6583399fa209 100644
--- a/utils/TableGen/InstrInfoEmitter.cpp
+++ b/utils/TableGen/InstrInfoEmitter.cpp
@@ -1,4 +1,4 @@
-//===- InstrInfoEmitter.cpp - Generate a Instruction Set Desc. ------------===//
+//===- InstrInfoEmitter.cpp - Generate a Instruction Set Desc. --*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
@@ -12,7 +12,6 @@
//
//===----------------------------------------------------------------------===//
-
#include "CodeGenDAGPatterns.h"
#include "CodeGenSchedule.h"
#include "CodeGenTarget.h"
@@ -26,6 +25,7 @@
#include <cstdio>
#include <map>
#include <vector>
+
using namespace llvm;
namespace {
@@ -70,11 +70,11 @@ private:
void EmitOperandInfo(raw_ostream &OS, OperandInfoMapTy &OperandInfoIDs);
std::vector<std::string> GetOperandInfo(const CodeGenInstruction &Inst);
};
-} // End anonymous namespace
+} // end anonymous namespace
static void PrintDefList(const std::vector<Record*> &Uses,
unsigned Num, raw_ostream &OS) {
- OS << "static const uint16_t ImplicitList" << Num << "[] = { ";
+ OS << "static const MCPhysReg ImplicitList" << Num << "[] = { ";
for (unsigned i = 0, e = Uses.size(); i != e; ++i)
OS << getQualifiedName(Uses[i]) << ", ";
OS << "0 };\n";
@@ -190,7 +190,6 @@ void InstrInfoEmitter::EmitOperandInfo(raw_ostream &OS,
}
}
-
/// Initialize data structures for generating operand name mappings.
///
/// \param Operands [out] A map used to generate the OpName enum with operand
@@ -257,9 +256,9 @@ void InstrInfoEmitter::emitOperandNameMappings(raw_ostream &OS,
OS << "OPERAND_LAST";
OS << "\n};\n";
- OS << "} // End namespace OpName\n";
- OS << "} // End namespace " << Namespace << "\n";
- OS << "} // End namespace llvm\n";
+ OS << "} // end namespace OpName\n";
+ OS << "} // end namespace " << Namespace << "\n";
+ OS << "} // end namespace llvm\n";
OS << "#endif //GET_INSTRINFO_OPERAND_ENUM\n";
OS << "#ifdef GET_INSTRINFO_NAMED_OPS\n";
@@ -298,8 +297,8 @@ void InstrInfoEmitter::emitOperandNameMappings(raw_ostream &OS,
OS << " return -1;\n";
}
OS << "}\n";
- OS << "} // End namespace " << Namespace << "\n";
- OS << "} // End namespace llvm\n";
+ OS << "} // end namespace " << Namespace << "\n";
+ OS << "} // end namespace llvm\n";
OS << "#endif //GET_INSTRINFO_NAMED_OPS\n";
}
@@ -328,9 +327,9 @@ void InstrInfoEmitter::emitOperandTypesEnum(raw_ostream &OS,
}
OS << " OPERAND_TYPE_LIST_END" << "\n};\n";
- OS << "} // End namespace OpTypes\n";
- OS << "} // End namespace " << Namespace << "\n";
- OS << "} // End namespace llvm\n";
+ OS << "} // end namespace OpTypes\n";
+ OS << "} // end namespace " << Namespace << "\n";
+ OS << "} // end namespace llvm\n";
OS << "#endif // GET_INSTRINFO_OPERAND_TYPES_ENUM\n";
}
@@ -419,7 +418,7 @@ void InstrInfoEmitter::run(raw_ostream &OS) {
<< TargetName << "InstrNameIndices, " << TargetName << "InstrNameData, "
<< NumberedInstructions.size() << ");\n}\n\n";
- OS << "} // End llvm namespace \n";
+ OS << "} // end llvm namespace \n";
OS << "#endif // GET_INSTRINFO_MC_DESC\n\n";
@@ -431,10 +430,10 @@ void InstrInfoEmitter::run(raw_ostream &OS) {
OS << "namespace llvm {\n";
OS << "struct " << ClassName << " : public TargetInstrInfo {\n"
<< " explicit " << ClassName
- << "(int CFSetupOpcode = -1, int CFDestroyOpcode = -1);\n"
- << " virtual ~" << ClassName << "();\n"
+ << "(int CFSetupOpcode = -1, int CFDestroyOpcode = -1, int CatchRetOpcode = -1);\n"
+ << " ~" << ClassName << "() override {}\n"
<< "};\n";
- OS << "} // End llvm namespace \n";
+ OS << "} // end llvm namespace \n";
OS << "#endif // GET_INSTRINFO_HEADER\n\n";
@@ -446,13 +445,12 @@ void InstrInfoEmitter::run(raw_ostream &OS) {
OS << "extern const unsigned " << TargetName << "InstrNameIndices[];\n";
OS << "extern const char " << TargetName << "InstrNameData[];\n";
OS << ClassName << "::" << ClassName
- << "(int CFSetupOpcode, int CFDestroyOpcode)\n"
- << " : TargetInstrInfo(CFSetupOpcode, CFDestroyOpcode) {\n"
+ << "(int CFSetupOpcode, int CFDestroyOpcode, int CatchRetOpcode)\n"
+ << " : TargetInstrInfo(CFSetupOpcode, CFDestroyOpcode, CatchRetOpcode) {\n"
<< " InitMCInstrInfo(" << TargetName << "Insts, " << TargetName
<< "InstrNameIndices, " << TargetName << "InstrNameData, "
- << NumberedInstructions.size() << ");\n}\n"
- << ClassName << "::~" << ClassName << "() {}\n";
- OS << "} // End llvm namespace \n";
+ << NumberedInstructions.size() << ");\n}\n";
+ OS << "} // end llvm namespace \n";
OS << "#endif // GET_INSTRINFO_CTOR_DTOR\n\n";
@@ -596,9 +594,9 @@ void InstrInfoEmitter::emitEnums(raw_ostream &OS) {
OS << " " << Class.Name << "\t= " << Num++ << ",\n";
OS << " SCHED_LIST_END = " << SchedModels.numInstrSchedClasses() << "\n";
OS << " };\n";
- OS << "} // End Sched namespace\n";
- OS << "} // End " << Namespace << " namespace\n";
- OS << "} // End llvm namespace \n";
+ OS << "} // end Sched namespace\n";
+ OS << "} // end " << Namespace << " namespace\n";
+ OS << "} // end llvm namespace \n";
OS << "#endif // GET_INSTRINFO_ENUM\n\n";
}
@@ -610,4 +608,4 @@ void EmitInstrInfo(RecordKeeper &RK, raw_ostream &OS) {
EmitMapTable(RK, OS);
}
-} // End llvm namespace
+} // end llvm namespace
diff --git a/utils/TableGen/IntrinsicEmitter.cpp b/utils/TableGen/IntrinsicEmitter.cpp
index 2b59ee69d16ac..42a6a152f55ec 100644
--- a/utils/TableGen/IntrinsicEmitter.cpp
+++ b/utils/TableGen/IntrinsicEmitter.cpp
@@ -50,8 +50,6 @@ public:
raw_ostream &OS);
void EmitAttributes(const std::vector<CodeGenIntrinsic> &Ints,
raw_ostream &OS);
- void EmitModRefBehavior(const std::vector<CodeGenIntrinsic> &Ints,
- raw_ostream &OS);
void EmitIntrinsicToGCCBuiltinMap(const std::vector<CodeGenIntrinsic> &Ints,
raw_ostream &OS);
void EmitIntrinsicToMSBuiltinMap(const std::vector<CodeGenIntrinsic> &Ints,
@@ -92,9 +90,6 @@ void IntrinsicEmitter::run(raw_ostream &OS) {
// Emit the intrinsic parameter attributes.
EmitAttributes(Ints, OS);
- // Emit intrinsic alias analysis mod/ref behavior.
- EmitModRefBehavior(Ints, OS);
-
// Emit code to translate GCC builtins into LLVM intrinsics.
EmitIntrinsicToGCCBuiltinMap(Ints, OS);
@@ -246,22 +241,25 @@ enum IIT_Info {
// Values from 16+ are only encodable with the inefficient encoding.
IIT_V64 = 16,
IIT_MMX = 17,
- IIT_METADATA = 18,
- IIT_EMPTYSTRUCT = 19,
- IIT_STRUCT2 = 20,
- IIT_STRUCT3 = 21,
- IIT_STRUCT4 = 22,
- IIT_STRUCT5 = 23,
- IIT_EXTEND_ARG = 24,
- IIT_TRUNC_ARG = 25,
- IIT_ANYPTR = 26,
- IIT_V1 = 27,
- IIT_VARARG = 28,
- IIT_HALF_VEC_ARG = 29,
- IIT_SAME_VEC_WIDTH_ARG = 30,
- IIT_PTR_TO_ARG = 31,
- IIT_VEC_OF_PTRS_TO_ELT = 32,
- IIT_I128 = 33
+ IIT_TOKEN = 18,
+ IIT_METADATA = 19,
+ IIT_EMPTYSTRUCT = 20,
+ IIT_STRUCT2 = 21,
+ IIT_STRUCT3 = 22,
+ IIT_STRUCT4 = 23,
+ IIT_STRUCT5 = 24,
+ IIT_EXTEND_ARG = 25,
+ IIT_TRUNC_ARG = 26,
+ IIT_ANYPTR = 27,
+ IIT_V1 = 28,
+ IIT_VARARG = 29,
+ IIT_HALF_VEC_ARG = 30,
+ IIT_SAME_VEC_WIDTH_ARG = 31,
+ IIT_PTR_TO_ARG = 32,
+ IIT_VEC_OF_PTRS_TO_ELT = 33,
+ IIT_I128 = 34,
+ IIT_V512 = 35,
+ IIT_V1024 = 36
};
@@ -285,6 +283,7 @@ static void EncodeFixedValueType(MVT::SimpleValueType VT,
case MVT::f16: return Sig.push_back(IIT_F16);
case MVT::f32: return Sig.push_back(IIT_F32);
case MVT::f64: return Sig.push_back(IIT_F64);
+ case MVT::token: return Sig.push_back(IIT_TOKEN);
case MVT::Metadata: return Sig.push_back(IIT_METADATA);
case MVT::x86mmx: return Sig.push_back(IIT_MMX);
// MVT::OtherVT is used to mean the empty struct type here.
@@ -375,6 +374,8 @@ static void EncodeFixedType(Record *R, std::vector<unsigned char> &ArgCodes,
case 16: Sig.push_back(IIT_V16); break;
case 32: Sig.push_back(IIT_V32); break;
case 64: Sig.push_back(IIT_V64); break;
+ case 512: Sig.push_back(IIT_V512); break;
+ case 1024: Sig.push_back(IIT_V1024); break;
}
return EncodeFixedValueType(VVT.getVectorElementType().SimpleTy, Sig);
@@ -503,28 +504,6 @@ void IntrinsicEmitter::EmitGenerator(const std::vector<CodeGenIntrinsic> &Ints,
}
namespace {
-enum ModRefKind {
- MRK_none,
- MRK_readonly,
- MRK_readnone
-};
-}
-
-static ModRefKind getModRefKind(const CodeGenIntrinsic &intrinsic) {
- switch (intrinsic.ModRef) {
- case CodeGenIntrinsic::NoMem:
- return MRK_readnone;
- case CodeGenIntrinsic::ReadArgMem:
- case CodeGenIntrinsic::ReadMem:
- return MRK_readonly;
- case CodeGenIntrinsic::ReadWriteArgMem:
- case CodeGenIntrinsic::ReadWriteMem:
- return MRK_none;
- }
- llvm_unreachable("bad mod-ref kind");
-}
-
-namespace {
struct AttributeComparator {
bool operator()(const CodeGenIntrinsic *L, const CodeGenIntrinsic *R) const {
// Sort throwing intrinsics after non-throwing intrinsics.
@@ -541,8 +520,8 @@ struct AttributeComparator {
return R->isConvergent;
// Try to order by readonly/readnone attribute.
- ModRefKind LK = getModRefKind(*L);
- ModRefKind RK = getModRefKind(*R);
+ CodeGenIntrinsic::ModRefKind LK = L->ModRef;
+ CodeGenIntrinsic::ModRefKind RK = R->ModRef;
if (LK != RK) return (LK > RK);
// Order by argument attributes.
@@ -636,7 +615,7 @@ EmitAttributes(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS) {
case CodeGenIntrinsic::ReadNone:
if (addComma)
OS << ",";
- OS << "Attributes::ReadNone";
+ OS << "Attribute::ReadNone";
addComma = true;
break;
}
@@ -649,10 +628,10 @@ EmitAttributes(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS) {
}
}
- ModRefKind modRef = getModRefKind(intrinsic);
-
- if (!intrinsic.canThrow || modRef || intrinsic.isNoReturn ||
- intrinsic.isNoDuplicate || intrinsic.isConvergent) {
+ if (!intrinsic.canThrow ||
+ intrinsic.ModRef != CodeGenIntrinsic::ReadWriteMem ||
+ intrinsic.isNoReturn || intrinsic.isNoDuplicate ||
+ intrinsic.isConvergent) {
OS << " const Attribute::AttrKind Atts[] = {";
bool addComma = false;
if (!intrinsic.canThrow) {
@@ -678,17 +657,29 @@ EmitAttributes(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS) {
addComma = true;
}
- switch (modRef) {
- case MRK_none: break;
- case MRK_readonly:
+ switch (intrinsic.ModRef) {
+ case CodeGenIntrinsic::NoMem:
+ if (addComma)
+ OS << ",";
+ OS << "Attribute::ReadNone";
+ break;
+ case CodeGenIntrinsic::ReadArgMem:
+ if (addComma)
+ OS << ",";
+ OS << "Attribute::ReadOnly,";
+ OS << "Attribute::ArgMemOnly";
+ break;
+ case CodeGenIntrinsic::ReadMem:
if (addComma)
OS << ",";
OS << "Attribute::ReadOnly";
break;
- case MRK_readnone:
+ case CodeGenIntrinsic::ReadWriteArgMem:
if (addComma)
OS << ",";
- OS << "Attribute::ReadNone";
+ OS << "Attribute::ArgMemOnly";
+ break;
+ case CodeGenIntrinsic::ReadWriteMem:
break;
}
OS << "};\n";
@@ -713,41 +704,6 @@ EmitAttributes(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS) {
OS << "#endif // GET_INTRINSIC_ATTRIBUTES\n\n";
}
-/// EmitModRefBehavior - Determine intrinsic alias analysis mod/ref behavior.
-void IntrinsicEmitter::
-EmitModRefBehavior(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS){
- OS << "// Determine intrinsic alias analysis mod/ref behavior.\n"
- << "#ifdef GET_INTRINSIC_MODREF_BEHAVIOR\n"
- << "assert(iid <= Intrinsic::" << Ints.back().EnumName << " && "
- << "\"Unknown intrinsic.\");\n\n";
-
- OS << "static const uint8_t IntrinsicModRefBehavior[] = {\n"
- << " /* invalid */ UnknownModRefBehavior,\n";
- for (unsigned i = 0, e = Ints.size(); i != e; ++i) {
- OS << " /* " << TargetPrefix << Ints[i].EnumName << " */ ";
- switch (Ints[i].ModRef) {
- case CodeGenIntrinsic::NoMem:
- OS << "DoesNotAccessMemory,\n";
- break;
- case CodeGenIntrinsic::ReadArgMem:
- OS << "OnlyReadsArgumentPointees,\n";
- break;
- case CodeGenIntrinsic::ReadMem:
- OS << "OnlyReadsMemory,\n";
- break;
- case CodeGenIntrinsic::ReadWriteArgMem:
- OS << "OnlyAccessesArgumentPointees,\n";
- break;
- case CodeGenIntrinsic::ReadWriteMem:
- OS << "UnknownModRefBehavior,\n";
- break;
- }
- }
- OS << "};\n\n"
- << "return static_cast<ModRefBehavior>(IntrinsicModRefBehavior[iid]);\n"
- << "#endif // GET_INTRINSIC_MODREF_BEHAVIOR\n\n";
-}
-
/// EmitTargetBuiltins - All of the builtins in the specified map are for the
/// same target, and we already checked it.
static void EmitTargetBuiltins(const std::map<std::string, std::string> &BIM,
diff --git a/utils/TableGen/OptParserEmitter.cpp b/utils/TableGen/OptParserEmitter.cpp
index 9262d7c8a02c6..c1b5e6510325c 100644
--- a/utils/TableGen/OptParserEmitter.cpp
+++ b/utils/TableGen/OptParserEmitter.cpp
@@ -149,10 +149,10 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS) {
PE = I->first.end(); PI != PE; ++PI) {
OS << "\"" << *PI << "\" COMMA ";
}
- OS << "0})\n";
+ OS << "nullptr})\n";
}
OS << "#undef COMMA\n";
- OS << "#endif\n\n";
+ OS << "#endif // PREFIX\n\n";
OS << "/////////\n";
OS << "// Groups\n\n";
@@ -164,7 +164,7 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS) {
OS << "OPTION(";
// The option prefix;
- OS << "0";
+ OS << "nullptr";
// The option string.
OS << ", \"" << R.getValueAsString("Name") << '"';
@@ -183,7 +183,7 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS) {
OS << "INVALID";
// The other option arguments (unused for groups).
- OS << ", INVALID, 0, 0, 0";
+ OS << ", INVALID, nullptr, 0, 0";
// The option help text.
if (!isa<UnsetInit>(R.getValueInit("HelpText"))) {
@@ -191,10 +191,10 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS) {
OS << " ";
write_cstring(OS, R.getValueAsString("HelpText"));
} else
- OS << ", 0";
+ OS << ", nullptr";
// The option meta-variable name (unused).
- OS << ", 0)\n";
+ OS << ", nullptr)\n";
}
OS << "\n";
@@ -242,7 +242,7 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS) {
OS << ", ";
std::vector<std::string> AliasArgs = R.getValueAsListOfStrings("AliasArgs");
if (AliasArgs.size() == 0) {
- OS << "0";
+ OS << "nullptr";
} else {
OS << "\"";
for (size_t i = 0, e = AliasArgs.size(); i != e; ++i)
@@ -274,17 +274,17 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS) {
OS << " ";
write_cstring(OS, R.getValueAsString("HelpText"));
} else
- OS << ", 0";
+ OS << ", nullptr";
// The option meta-variable name.
OS << ", ";
if (!isa<UnsetInit>(R.getValueInit("MetaVarName")))
write_cstring(OS, R.getValueAsString("MetaVarName"));
else
- OS << "0";
+ OS << "nullptr";
OS << ")\n";
}
- OS << "#endif\n";
+ OS << "#endif // OPTION\n";
}
} // end namespace llvm
diff --git a/utils/TableGen/RegisterInfoEmitter.cpp b/utils/TableGen/RegisterInfoEmitter.cpp
index 9619fb9e356e9..b727df75626f2 100644
--- a/utils/TableGen/RegisterInfoEmitter.cpp
+++ b/utils/TableGen/RegisterInfoEmitter.cpp
@@ -240,7 +240,7 @@ EmitRegUnitPressure(raw_ostream &OS, const CodeGenRegBank &RegBank,
MaxRegUnitWeight = std::max(MaxRegUnitWeight, RegUnits.Weight);
OS << " \"" << RegUnits.Name << "\",\n";
}
- OS << " nullptr };\n"
+ OS << " };\n"
<< " return PressureNameTable[Idx];\n"
<< "}\n\n";
@@ -1074,9 +1074,7 @@ RegisterInfoEmitter::runTargetHeader(raw_ostream &OS, CodeGenTarget &Target,
OS << "struct " << ClassName << " : public TargetRegisterInfo {\n"
<< " explicit " << ClassName
- << "(unsigned RA, unsigned D = 0, unsigned E = 0, unsigned PC = 0);\n"
- << " bool needsStackRealignment(const MachineFunction &) const override\n"
- << " { return false; }\n";
+ << "(unsigned RA, unsigned D = 0, unsigned E = 0, unsigned PC = 0);\n";
if (!RegBank.getSubRegIndices().empty()) {
OS << " unsigned composeSubRegIndicesImpl"
<< "(unsigned, unsigned) const override;\n"
@@ -1454,27 +1452,32 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target,
OS << "ArrayRef<const uint32_t *> " << ClassName
<< "::getRegMasks() const {\n";
- OS << " static const uint32_t *Masks[] = {\n";
- for (Record *CSRSet : CSRSets)
- OS << " " << CSRSet->getName() << "_RegMask, \n";
- OS << " nullptr\n };\n";
- OS << " return ArrayRef<const uint32_t *>(Masks, (size_t)" << CSRSets.size()
- << ");\n";
+ if (!CSRSets.empty()) {
+ OS << " static const uint32_t *const Masks[] = {\n";
+ for (Record *CSRSet : CSRSets)
+ OS << " " << CSRSet->getName() << "_RegMask,\n";
+ OS << " };\n";
+ OS << " return makeArrayRef(Masks);\n";
+ } else {
+ OS << " return None;\n";
+ }
OS << "}\n\n";
OS << "ArrayRef<const char *> " << ClassName
<< "::getRegMaskNames() const {\n";
- OS << " static const char *Names[] = {\n";
- for (Record *CSRSet : CSRSets)
- OS << " " << '"' << CSRSet->getName() << '"' << ",\n";
- OS << " nullptr\n };\n";
- OS << " return ArrayRef<const char *>(Names, (size_t)" << CSRSets.size()
- << ");\n";
+ if (!CSRSets.empty()) {
+ OS << " static const char *const Names[] = {\n";
+ for (Record *CSRSet : CSRSets)
+ OS << " " << '"' << CSRSet->getName() << '"' << ",\n";
+ OS << " };\n";
+ OS << " return makeArrayRef(Names);\n";
+ } else {
+ OS << " return None;\n";
+ }
OS << "}\n\n";
- OS << "const " << TargetName << "FrameLowering *"
- << TargetName << "GenRegisterInfo::\n"
- << " getFrameLowering(const MachineFunction &MF) {\n"
+ OS << "const " << TargetName << "FrameLowering *\n" << TargetName
+ << "GenRegisterInfo::getFrameLowering(const MachineFunction &MF) {\n"
<< " return static_cast<const " << TargetName << "FrameLowering *>(\n"
<< " MF.getSubtarget().getFrameLowering());\n"
<< "}\n\n";
diff --git a/utils/TableGen/SubtargetEmitter.cpp b/utils/TableGen/SubtargetEmitter.cpp
index 03d7f4e868e78..6246d811123de 100644
--- a/utils/TableGen/SubtargetEmitter.cpp
+++ b/utils/TableGen/SubtargetEmitter.cpp
@@ -26,6 +26,7 @@
#include <map>
#include <string>
#include <vector>
+
using namespace llvm;
#define DEBUG_TYPE "subtarget-emitter"
@@ -105,9 +106,8 @@ public:
Records(R), SchedModels(TGT.getSchedModels()), Target(TGT.getName()) {}
void run(raw_ostream &o);
-
};
-} // End anonymous namespace
+} // end anonymous namespace
//
// Enumeration - Emit the specified class as an enumeration.
@@ -1199,7 +1199,8 @@ void SubtargetEmitter::EmitProcessorModels(raw_ostream &OS) {
<< " " << (SchedModels.schedClassEnd()
- SchedModels.schedClassBegin()) << ",\n";
else
- OS << " 0, 0, 0, 0, // No instruction-level machine model.\n";
+ OS << " nullptr, nullptr, 0, 0,"
+ << " // No instruction-level machine model.\n";
if (PI->hasItineraries())
OS << " " << PI->ItinsDef->getName() << "};\n";
else
@@ -1414,7 +1415,7 @@ void SubtargetEmitter::run(raw_ostream &OS) {
OS << "namespace llvm {\n";
Enumeration(OS, "SubtargetFeature");
- OS << "} // End llvm namespace \n";
+ OS << "} // end llvm namespace\n";
OS << "#endif // GET_SUBTARGETINFO_ENUM\n\n";
OS << "\n#ifdef GET_SUBTARGETINFO_MC_DESC\n";
@@ -1461,7 +1462,7 @@ void SubtargetEmitter::run(raw_ostream &OS) {
OS << "0, 0, 0";
OS << ");\n}\n\n";
- OS << "} // End llvm namespace \n";
+ OS << "} // end llvm namespace\n";
OS << "#endif // GET_SUBTARGETINFO_MC_DESC\n\n";
@@ -1491,7 +1492,7 @@ void SubtargetEmitter::run(raw_ostream &OS) {
<< " DFAPacketizer *createDFAPacketizer(const InstrItineraryData *IID)"
<< " const;\n"
<< "};\n";
- OS << "} // End llvm namespace \n";
+ OS << "} // end llvm namespace\n";
OS << "#endif // GET_SUBTARGETINFO_HEADER\n\n";
@@ -1543,7 +1544,7 @@ void SubtargetEmitter::run(raw_ostream &OS) {
EmitSchedModelHelpers(ClassName, OS);
- OS << "} // End llvm namespace \n";
+ OS << "} // end llvm namespace\n";
OS << "#endif // GET_SUBTARGETINFO_CTOR\n\n";
}
@@ -1555,4 +1556,4 @@ void EmitSubtarget(RecordKeeper &RK, raw_ostream &OS) {
SubtargetEmitter(RK, CGTarget).run(OS);
}
-} // End llvm namespace
+} // end llvm namespace
diff --git a/utils/TableGen/TableGen.cpp b/utils/TableGen/TableGen.cpp
index 02fe4dc98bea6..c16a5583eb365 100644
--- a/utils/TableGen/TableGen.cpp
+++ b/utils/TableGen/TableGen.cpp
@@ -41,7 +41,8 @@ enum ActionType {
PrintEnums,
PrintSets,
GenOptParserDefs,
- GenCTags
+ GenCTags,
+ GenAttributes
};
namespace {
@@ -85,6 +86,8 @@ namespace {
"Generate option definitions"),
clEnumValN(GenCTags, "gen-ctags",
"Generate ctags-compatible index"),
+ clEnumValN(GenAttributes, "gen-attrs",
+ "Generate attributes"),
clEnumValEnd));
cl::opt<std::string>
@@ -165,6 +168,9 @@ bool LLVMTableGenMain(raw_ostream &OS, RecordKeeper &Records) {
case GenCTags:
EmitCTags(Records, OS);
break;
+ case GenAttributes:
+ EmitAttributes(Records, OS);
+ break;
}
return false;
diff --git a/utils/TableGen/TableGenBackends.h b/utils/TableGen/TableGenBackends.h
index 2dc03ce1e71e4..d9dd3d157697b 100644
--- a/utils/TableGen/TableGenBackends.h
+++ b/utils/TableGen/TableGenBackends.h
@@ -78,6 +78,7 @@ void EmitSubtarget(RecordKeeper &RK, raw_ostream &OS);
void EmitMapTable(RecordKeeper &RK, raw_ostream &OS);
void EmitOptParser(RecordKeeper &RK, raw_ostream &OS);
void EmitCTags(RecordKeeper &RK, raw_ostream &OS);
+void EmitAttributes(RecordKeeper &RK, raw_ostream &OS);
} // End llvm namespace
diff --git a/utils/TableGen/X86RecognizableInstr.cpp b/utils/TableGen/X86RecognizableInstr.cpp
index efcb0c81e1caf..8a5ae12f67fb2 100644
--- a/utils/TableGen/X86RecognizableInstr.cpp
+++ b/utils/TableGen/X86RecognizableInstr.cpp
@@ -796,12 +796,12 @@ void RecognizableInstr::emitInstructionSpecifier() {
case X86Local::MRM_E3: case X86Local::MRM_E4: case X86Local::MRM_E5:
case X86Local::MRM_E8: case X86Local::MRM_E9: case X86Local::MRM_EA:
case X86Local::MRM_EB: case X86Local::MRM_EC: case X86Local::MRM_ED:
- case X86Local::MRM_EE: case X86Local::MRM_F0: case X86Local::MRM_F1:
- case X86Local::MRM_F2: case X86Local::MRM_F3: case X86Local::MRM_F4:
- case X86Local::MRM_F5: case X86Local::MRM_F6: case X86Local::MRM_F7:
- case X86Local::MRM_F9: case X86Local::MRM_FA: case X86Local::MRM_FB:
- case X86Local::MRM_FC: case X86Local::MRM_FD: case X86Local::MRM_FE:
- case X86Local::MRM_FF:
+ case X86Local::MRM_EE: case X86Local::MRM_EF: case X86Local::MRM_F0:
+ case X86Local::MRM_F1: case X86Local::MRM_F2: case X86Local::MRM_F3:
+ case X86Local::MRM_F4: case X86Local::MRM_F5: case X86Local::MRM_F6:
+ case X86Local::MRM_F7: case X86Local::MRM_F9: case X86Local::MRM_FA:
+ case X86Local::MRM_FB: case X86Local::MRM_FC: case X86Local::MRM_FD:
+ case X86Local::MRM_FE: case X86Local::MRM_FF:
// Ignored.
break;
}
@@ -951,6 +951,7 @@ OperandType RecognizableInstr::typeFromString(const std::string &s,
TYPE("f128mem", TYPE_M128)
TYPE("f256mem", TYPE_M256)
TYPE("f512mem", TYPE_M512)
+ TYPE("FR128", TYPE_XMM128)
TYPE("FR64", TYPE_XMM64)
TYPE("FR64X", TYPE_XMM64)
TYPE("f64mem", TYPE_M64FP)
@@ -1069,6 +1070,7 @@ RecognizableInstr::immediateEncodingFromString(const std::string &s,
// register IDs in 8-bit immediates nowadays.
ENCODING("FR32", ENCODING_IB)
ENCODING("FR64", ENCODING_IB)
+ ENCODING("FR128", ENCODING_IB)
ENCODING("VR128", ENCODING_IB)
ENCODING("VR256", ENCODING_IB)
ENCODING("FR32X", ENCODING_IB)
@@ -1091,6 +1093,7 @@ RecognizableInstr::rmRegisterEncodingFromString(const std::string &s,
ENCODING("GR8", ENCODING_RM)
ENCODING("VR128", ENCODING_RM)
ENCODING("VR128X", ENCODING_RM)
+ ENCODING("FR128", ENCODING_RM)
ENCODING("FR64", ENCODING_RM)
ENCODING("FR32", ENCODING_RM)
ENCODING("FR64X", ENCODING_RM)
@@ -1120,6 +1123,7 @@ RecognizableInstr::roRegisterEncodingFromString(const std::string &s,
ENCODING("GR64", ENCODING_REG)
ENCODING("GR8", ENCODING_REG)
ENCODING("VR128", ENCODING_REG)
+ ENCODING("FR128", ENCODING_REG)
ENCODING("FR64", ENCODING_REG)
ENCODING("FR32", ENCODING_REG)
ENCODING("VR64", ENCODING_REG)
@@ -1157,6 +1161,7 @@ RecognizableInstr::vvvvRegisterEncodingFromString(const std::string &s,
ENCODING("GR32", ENCODING_VVVV)
ENCODING("GR64", ENCODING_VVVV)
ENCODING("FR32", ENCODING_VVVV)
+ ENCODING("FR128", ENCODING_VVVV)
ENCODING("FR64", ENCODING_VVVV)
ENCODING("VR128", ENCODING_VVVV)
ENCODING("VR256", ENCODING_VVVV)
diff --git a/utils/emacs/llvm-mode.el b/utils/emacs/llvm-mode.el
index 5fb1eb38cc62d..3a609f2490542 100644
--- a/utils/emacs/llvm-mode.el
+++ b/utils/emacs/llvm-mode.el
@@ -8,22 +8,25 @@
;;; Code:
-(defvar llvm-mode-syntax-table nil
+(defvar llvm-mode-syntax-table
+ (let ((table (make-syntax-table)))
+ (modify-syntax-entry ?% "_" table)
+ (modify-syntax-entry ?. "_" table)
+ (modify-syntax-entry ?\; "< " table)
+ (modify-syntax-entry ?\n "> " table)
+ table)
"Syntax table used while in LLVM mode.")
+
(defvar llvm-font-lock-keywords
(list
- ;; Comments
- '(";.*" . font-lock-comment-face)
;; Variables
'("%[-a-zA-Z$\._][-a-zA-Z$\._0-9]*" . font-lock-variable-name-face)
;; Labels
'("[-a-zA-Z$\._0-9]+:" . font-lock-variable-name-face)
- ;; Strings
- '("\"[^\"]+\"" . font-lock-string-face)
;; Unnamed variable slots
'("%[-]?[0-9]+" . font-lock-variable-name-face)
;; Types
- `(,(regexp-opt '("void" "i1" "i8" "i16" "i32" "i64" "i128" "float" "double" "type" "label" "opaque") 'symbol) . font-lock-type-face)
+ `(,(regexp-opt '("void" "i1" "i8" "i16" "i32" "i64" "i128" "float" "double" "type" "label" "opaque") 'symbols) . font-lock-type-face)
;; Integer literals
'("\\b[-]?[0-9]+\\b" . font-lock-preprocessor-face)
;; Floating point constants
@@ -56,84 +59,22 @@
;; Metadata types
`(,(regexp-opt '("distinct") 'symbols) . font-lock-keyword-face)
;; Use-list order directives
- `(,(regexp-opt '("uselistorder" "uselistorder_bb") 'symbols) . font-lock-keyword-face)
-
- )
- "Syntax highlighting for LLVM."
- )
-
-;; ---------------------- Syntax table ---------------------------
-;; Shamelessly ripped from jasmin.el
-;; URL: http://www.neilvandyke.org/jasmin-emacs/jasmin.el.html
-
-(if (not llvm-mode-syntax-table)
- (progn
- (setq llvm-mode-syntax-table (make-syntax-table))
- (mapc (function (lambda (n)
- (modify-syntax-entry (aref n 0)
- (aref n 1)
- llvm-mode-syntax-table)))
- '(
- ;; whitespace (` ')
- [?\^m " "]
- [?\f " "]
- [?\n " "]
- [?\t " "]
- [?\ " "]
- ;; word constituents (`w')
- ;;[?< "w"]
- ;;[?> "w"]
- [?% "w"]
- ;;[?_ "w "]
- ;; comments
- [?\; "< "]
- [?\n "> "]
- ;;[?\r "> "]
- ;;[?\^m "> "]
- ;; symbol constituents (`_')
- ;; punctuation (`.')
- ;; open paren (`(')
- ;; close paren (`)')
- ;; string quote ('"')
- [?\" "\""]))))
-
-;; --------------------- Abbrev table -----------------------------
+ `(,(regexp-opt '("uselistorder" "uselistorder_bb") 'symbols) . font-lock-keyword-face))
+ "Syntax highlighting for LLVM.")
-(defvar llvm-mode-abbrev-table nil
- "Abbrev table used while in LLVM mode.")
-(define-abbrev-table 'llvm-mode-abbrev-table ())
-
-(defvar llvm-mode-hook nil)
-(defvar llvm-mode-map nil) ; Create a mode-specific keymap.
-
-(if (not llvm-mode-map)
- () ; Do not change the keymap if it is already set up.
- (setq llvm-mode-map (make-sparse-keymap))
- (define-key llvm-mode-map "\t" 'tab-to-tab-stop)
- (define-key llvm-mode-map "\es" 'center-line)
- (define-key llvm-mode-map "\eS" 'center-paragraph))
+;; Emacs 23 compatibility.
+(defalias 'llvm-mode-prog-mode
+ (if (fboundp 'prog-mode)
+ 'prog-mode
+ 'fundamental-mode))
;;;###autoload
-(defun llvm-mode ()
+(define-derived-mode llvm-mode llvm-mode-prog-mode "LLVM"
"Major mode for editing LLVM source files.
\\{llvm-mode-map}
Runs `llvm-mode-hook' on startup."
- (interactive)
- (kill-all-local-variables)
- (use-local-map llvm-mode-map) ; Provides the local keymap.
- (setq major-mode 'llvm-mode)
-
- (make-local-variable 'font-lock-defaults)
- (setq major-mode 'llvm-mode ; This is how describe-mode
- ; finds the doc string to print.
- mode-name "LLVM" ; This name goes into the modeline.
- font-lock-defaults `(llvm-font-lock-keywords))
-
- (setq local-abbrev-table llvm-mode-abbrev-table)
- (set-syntax-table llvm-mode-syntax-table)
- (setq comment-start ";")
- (run-hooks 'llvm-mode-hook)) ; Finally, this permits the user to
- ; customize the mode with a hook.
+ (setq font-lock-defaults `(llvm-font-lock-keywords))
+ (setq comment-start ";"))
;; Associate .ll files with llvm-mode
;;;###autoload
diff --git a/utils/lit/TODO b/utils/lit/TODO
index 90da327be9a21..f94a6ba6bb083 100644
--- a/utils/lit/TODO
+++ b/utils/lit/TODO
@@ -158,7 +158,17 @@ Miscellaneous
* Support valgrind in all configs, and LLVM style valgrind.
-* Support a timeout / ulimit.
+* Support ulimit.
* Create an explicit test suite object (instead of using the top-level
TestingConfig object).
+
+* Introduce a wrapper class that has a ``subprocess.Popen`` like interface
+ but also supports killing the process and all its children and use this for
+ running tests. This would allow us to implement platform specific methods
+ for killing a process's children which is needed for a per test timeout. On
+ POSIX platforms we can use process groups and on Windows we can probably use
+ job objects. This would not only allow us to remove the dependency on the
+ ``psutil`` module but would also be more reliable as the
+ ``lit.util.killProcessAndChildren()`` function which is currently used is
+ potentially racey (e.g. it might not kill a fork bomb completely).
diff --git a/utils/lit/lit/LitConfig.py b/utils/lit/lit/LitConfig.py
index b8183801bfca7..2402221bef12d 100644
--- a/utils/lit/lit/LitConfig.py
+++ b/utils/lit/lit/LitConfig.py
@@ -8,7 +8,8 @@ import lit.formats
import lit.TestingConfig
import lit.util
-class LitConfig:
+# LitConfig must be a new style class for properties to work
+class LitConfig(object):
"""LitConfig - Configuration data for a 'lit' test runner instance, shared
across all tests.
@@ -21,7 +22,8 @@ class LitConfig:
def __init__(self, progname, path, quiet,
useValgrind, valgrindLeakCheck, valgrindArgs,
noExecute, debug, isWindows,
- params, config_prefix = None):
+ params, config_prefix = None,
+ maxIndividualTestTime = 0):
# The name of the test runner.
self.progname = progname
# The items to add to the PATH environment variable.
@@ -57,6 +59,36 @@ class LitConfig:
self.valgrindArgs.append('--leak-check=no')
self.valgrindArgs.extend(self.valgrindUserArgs)
+ self.maxIndividualTestTime = maxIndividualTestTime
+
+ @property
+ def maxIndividualTestTime(self):
+ """
+ Interface for getting maximum time to spend executing
+ a single test
+ """
+ return self._maxIndividualTestTime
+
+ @maxIndividualTestTime.setter
+ def maxIndividualTestTime(self, value):
+ """
+ Interface for setting maximum time to spend executing
+ a single test
+ """
+ self._maxIndividualTestTime = value
+ if self.maxIndividualTestTime > 0:
+ # The current implementation needs psutil to set
+ # a timeout per test. Check it's available.
+ # See lit.util.killProcessAndChildren()
+ try:
+ import psutil
+ except ImportError:
+ self.fatal("Setting a timeout per test requires the"
+ " Python psutil module but it could not be"
+ " found. Try installing it via pip or via"
+ " your operating system's package manager.")
+ elif self.maxIndividualTestTime < 0:
+ self.fatal('The timeout per test must be >= 0 seconds')
def load_config(self, config, path):
"""load_config(config, path) - Load a config object from an alternate
diff --git a/utils/lit/lit/Test.py b/utils/lit/lit/Test.py
index 38bb41b0252d5..ef0e7bfc2a37d 100644
--- a/utils/lit/lit/Test.py
+++ b/utils/lit/lit/Test.py
@@ -27,11 +27,13 @@ class ResultCode(object):
(self.name, self.isFailure))
PASS = ResultCode('PASS', False)
+FLAKYPASS = ResultCode('FLAKYPASS', False)
XFAIL = ResultCode('XFAIL', False)
FAIL = ResultCode('FAIL', True)
XPASS = ResultCode('XPASS', True)
UNRESOLVED = ResultCode('UNRESOLVED', True)
UNSUPPORTED = ResultCode('UNSUPPORTED', False)
+TIMEOUT = ResultCode('TIMEOUT', True)
# Test metric values.
@@ -253,4 +255,4 @@ class Test:
xml += "\n\t</failure>\n</testcase>"
else:
xml += "/>"
- return xml \ No newline at end of file
+ return xml
diff --git a/utils/lit/lit/TestRunner.py b/utils/lit/lit/TestRunner.py
index 70382b472f5c8..1af82e1584456 100644
--- a/utils/lit/lit/TestRunner.py
+++ b/utils/lit/lit/TestRunner.py
@@ -3,6 +3,7 @@ import os, signal, subprocess, sys
import re
import platform
import tempfile
+import threading
import lit.ShUtil as ShUtil
import lit.Test as Test
@@ -31,30 +32,129 @@ class ShellEnvironment(object):
def __init__(self, cwd, env):
self.cwd = cwd
- self.env = env
+ self.env = dict(env)
+
+class TimeoutHelper(object):
+ """
+ Object used to helper manage enforcing a timeout in
+ _executeShCmd(). It is passed through recursive calls
+ to collect processes that have been executed so that when
+ the timeout happens they can be killed.
+ """
+ def __init__(self, timeout):
+ self.timeout = timeout
+ self._procs = []
+ self._timeoutReached = False
+ self._doneKillPass = False
+ # This lock will be used to protect concurrent access
+ # to _procs and _doneKillPass
+ self._lock = None
+ self._timer = None
+
+ def cancel(self):
+ if not self.active():
+ return
+ self._timer.cancel()
+
+ def active(self):
+ return self.timeout > 0
+
+ def addProcess(self, proc):
+ if not self.active():
+ return
+ needToRunKill = False
+ with self._lock:
+ self._procs.append(proc)
+ # Avoid re-entering the lock by finding out if kill needs to be run
+ # again here but call it if necessary once we have left the lock.
+ # We could use a reentrant lock here instead but this code seems
+ # clearer to me.
+ needToRunKill = self._doneKillPass
+
+ # The initial call to _kill() from the timer thread already happened so
+ # we need to call it again from this thread, otherwise this process
+ # will be left to run even though the timeout was already hit
+ if needToRunKill:
+ assert self.timeoutReached()
+ self._kill()
+
+ def startTimer(self):
+ if not self.active():
+ return
+
+ # Do some late initialisation that's only needed
+ # if there is a timeout set
+ self._lock = threading.Lock()
+ self._timer = threading.Timer(self.timeout, self._handleTimeoutReached)
+ self._timer.start()
+
+ def _handleTimeoutReached(self):
+ self._timeoutReached = True
+ self._kill()
+
+ def timeoutReached(self):
+ return self._timeoutReached
+
+ def _kill(self):
+ """
+ This method may be called multiple times as we might get unlucky
+ and be in the middle of creating a new process in _executeShCmd()
+ which won't yet be in ``self._procs``. By locking here and in
+ addProcess() we should be able to kill processes launched after
+ the initial call to _kill()
+ """
+ with self._lock:
+ for p in self._procs:
+ lit.util.killProcessAndChildren(p.pid)
+ # Empty the list and note that we've done a pass over the list
+ self._procs = [] # Python2 doesn't have list.clear()
+ self._doneKillPass = True
+
+def executeShCmd(cmd, shenv, results, timeout=0):
+ """
+ Wrapper around _executeShCmd that handles
+ timeout
+ """
+ # Use the helper even when no timeout is required to make
+ # other code simpler (i.e. avoid bunch of ``!= None`` checks)
+ timeoutHelper = TimeoutHelper(timeout)
+ if timeout > 0:
+ timeoutHelper.startTimer()
+ finalExitCode = _executeShCmd(cmd, shenv, results, timeoutHelper)
+ timeoutHelper.cancel()
+ timeoutInfo = None
+ if timeoutHelper.timeoutReached():
+ timeoutInfo = 'Reached timeout of {} seconds'.format(timeout)
+
+ return (finalExitCode, timeoutInfo)
+
+def _executeShCmd(cmd, shenv, results, timeoutHelper):
+ if timeoutHelper.timeoutReached():
+ # Prevent further recursion if the timeout has been hit
+ # as we should try avoid launching more processes.
+ return None
-def executeShCmd(cmd, shenv, results):
if isinstance(cmd, ShUtil.Seq):
if cmd.op == ';':
- res = executeShCmd(cmd.lhs, shenv, results)
- return executeShCmd(cmd.rhs, shenv, results)
+ res = _executeShCmd(cmd.lhs, shenv, results, timeoutHelper)
+ return _executeShCmd(cmd.rhs, shenv, results, timeoutHelper)
if cmd.op == '&':
raise InternalShellError(cmd,"unsupported shell operator: '&'")
if cmd.op == '||':
- res = executeShCmd(cmd.lhs, shenv, results)
+ res = _executeShCmd(cmd.lhs, shenv, results, timeoutHelper)
if res != 0:
- res = executeShCmd(cmd.rhs, shenv, results)
+ res = _executeShCmd(cmd.rhs, shenv, results, timeoutHelper)
return res
if cmd.op == '&&':
- res = executeShCmd(cmd.lhs, shenv, results)
+ res = _executeShCmd(cmd.lhs, shenv, results, timeoutHelper)
if res is None:
return res
if res == 0:
- res = executeShCmd(cmd.rhs, shenv, results)
+ res = _executeShCmd(cmd.rhs, shenv, results, timeoutHelper)
return res
raise ValueError('Unknown shell command: %r' % cmd.op)
@@ -62,14 +162,18 @@ def executeShCmd(cmd, shenv, results):
# Handle shell builtins first.
if cmd.commands[0].args[0] == 'cd':
- # Update the cwd in the environment.
+ if len(cmd.commands) != 1:
+ raise ValueError("'cd' cannot be part of a pipeline")
if len(cmd.commands[0].args) != 2:
- raise ValueError('cd supports only one argument')
+ raise ValueError("'cd' supports only one argument")
newdir = cmd.commands[0].args[1]
+ # Update the cwd in the parent environment.
if os.path.isabs(newdir):
shenv.cwd = newdir
else:
shenv.cwd = os.path.join(shenv.cwd, newdir)
+ # The cd builtin always succeeds. If the directory does not exist, the
+ # following Popen calls will fail instead.
return 0
procs = []
@@ -81,6 +185,23 @@ def executeShCmd(cmd, shenv, results):
# output. This is null until we have seen some output using
# stderr.
for i,j in enumerate(cmd.commands):
+ # Reference the global environment by default.
+ cmd_shenv = shenv
+ if j.args[0] == 'env':
+ # Create a copy of the global environment and modify it for this one
+ # command. There might be multiple envs in a pipeline:
+ # env FOO=1 llc < %s | env BAR=2 llvm-mc | FileCheck %s
+ cmd_shenv = ShellEnvironment(shenv.cwd, shenv.env)
+ arg_idx = 1
+ for arg_idx, arg in enumerate(j.args[1:]):
+ # Partition the string into KEY=VALUE.
+ key, eq, val = arg.partition('=')
+ # Stop if there was no equals.
+ if eq == '':
+ break
+ cmd_shenv.env[key] = val
+ j.args = j.args[arg_idx+1:]
+
# Apply the redirections, we use (N,) as a sentinel 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]
@@ -126,7 +247,7 @@ def executeShCmd(cmd, shenv, results):
r[2] = tempfile.TemporaryFile(mode=r[1])
else:
# Make sure relative paths are relative to the cwd.
- redir_filename = os.path.join(shenv.cwd, r[0])
+ redir_filename = os.path.join(cmd_shenv.cwd, r[0])
r[2] = open(redir_filename, r[1])
# Workaround a Win32 and/or subprocess bug when appending.
#
@@ -157,7 +278,14 @@ def executeShCmd(cmd, shenv, results):
# Resolve the executable path ourselves.
args = list(j.args)
- executable = lit.util.which(args[0], shenv.env['PATH'])
+ executable = None
+ # For paths relative to cwd, use the cwd of the shell environment.
+ if args[0].startswith('.'):
+ exe_in_cwd = os.path.join(cmd_shenv.cwd, args[0])
+ if os.path.isfile(exe_in_cwd):
+ executable = exe_in_cwd
+ if not executable:
+ executable = lit.util.which(args[0], cmd_shenv.env['PATH'])
if not executable:
raise InternalShellError(j, '%r: command not found' % j.args[0])
@@ -171,15 +299,17 @@ def executeShCmd(cmd, shenv, results):
args[i] = f.name
try:
- procs.append(subprocess.Popen(args, cwd=shenv.cwd,
+ procs.append(subprocess.Popen(args, cwd=cmd_shenv.cwd,
executable = executable,
stdin = stdin,
stdout = stdout,
stderr = stderr,
- env = shenv.env,
+ env = cmd_shenv.env,
close_fds = kUseCloseFDs))
+ # Let the helper know about this process
+ timeoutHelper.addProcess(procs[-1])
except OSError as e:
- raise InternalShellError(j, 'Could not create process due to {}'.format(e))
+ raise InternalShellError(j, 'Could not create process ({}) due to {}'.format(executable, e))
# Immediately close stdin for any process taking stdin from us.
if stdin == subprocess.PIPE:
@@ -243,7 +373,7 @@ def executeShCmd(cmd, shenv, results):
except:
err = str(err)
- results.append((cmd.commands[i], out, err, res))
+ results.append((cmd.commands[i], out, err, res, timeoutHelper.timeoutReached()))
if cmd.pipe_err:
# Python treats the exit code as a signed char.
if exitCode is None:
@@ -281,22 +411,25 @@ def executeScriptInternal(test, litConfig, tmpBase, commands, cwd):
cmd = ShUtil.Seq(cmd, '&&', c)
results = []
+ timeoutInfo = None
try:
shenv = ShellEnvironment(cwd, test.config.environment)
- exitCode = executeShCmd(cmd, shenv, results)
+ exitCode, timeoutInfo = executeShCmd(cmd, shenv, results, timeout=litConfig.maxIndividualTestTime)
except InternalShellError:
e = sys.exc_info()[1]
exitCode = 127
- results.append((e.command, '', e.message, exitCode))
+ results.append((e.command, '', e.message, exitCode, False))
out = err = ''
- for i,(cmd, cmd_out,cmd_err,res) in enumerate(results):
+ for i,(cmd, cmd_out, cmd_err, res, timeoutReached) in enumerate(results):
out += 'Command %d: %s\n' % (i, ' '.join('"%s"' % s for s in cmd.args))
out += 'Command %d Result: %r\n' % (i, res)
+ if litConfig.maxIndividualTestTime > 0:
+ out += 'Command %d Reached Timeout: %s\n\n' % (i, str(timeoutReached))
out += 'Command %d Output:\n%s\n\n' % (i, cmd_out)
out += 'Command %d Stderr:\n%s\n\n' % (i, cmd_err)
- return out, err, exitCode
+ return out, err, exitCode, timeoutInfo
def executeScript(test, litConfig, tmpBase, commands, cwd):
bashPath = litConfig.getBashPath();
@@ -331,10 +464,15 @@ def executeScript(test, litConfig, tmpBase, commands, cwd):
# run on clang with no real loss.
command = litConfig.valgrindArgs + command
- return lit.util.executeCommand(command, cwd=cwd,
- env=test.config.environment)
+ try:
+ out, err, exitCode = lit.util.executeCommand(command, cwd=cwd,
+ env=test.config.environment,
+ timeout=litConfig.maxIndividualTestTime)
+ return (out, err, exitCode, None)
+ except lit.util.ExecuteCommandTimeoutException as e:
+ return (e.out, e.err, e.exitCode, e.msg)
-def parseIntegratedTestScriptCommands(source_path):
+def parseIntegratedTestScriptCommands(source_path, keywords):
"""
parseIntegratedTestScriptCommands(source_path) -> commands
@@ -353,7 +491,6 @@ def parseIntegratedTestScriptCommands(source_path):
# remaining code can work with "strings" agnostic of the executing Python
# version.
- keywords = ['RUN:', 'XFAIL:', 'REQUIRES:', 'UNSUPPORTED:', 'END.']
keywords_re = re.compile(
to_bytes("(%s)(.*)\n" % ("|".join(k for k in keywords),)))
@@ -387,27 +524,18 @@ def parseIntegratedTestScriptCommands(source_path):
finally:
f.close()
-
-def parseIntegratedTestScript(test, normalize_slashes=False,
- extra_substitutions=[], require_script=True):
- """parseIntegratedTestScript - Scan an LLVM/Clang style integrated test
- script and extract the lines to 'RUN' as well as 'XFAIL' and 'REQUIRES'
- and 'UNSUPPORTED' information. The RUN lines also will have variable
- substitution performed. If 'require_script' is False an empty script may be
- returned. This can be used for test formats where the actual script is
- optional or ignored.
- """
-
- # 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()
- sourcedir = os.path.dirname(sourcepath)
+def getTempPaths(test):
+ """Get the temporary location, this is always relative to the test suite
+ root, not test source root."""
execpath = test.getExecPath()
execdir,execbase = os.path.split(execpath)
tmpDir = os.path.join(execdir, 'Output')
tmpBase = os.path.join(tmpDir, execbase)
+ return tmpDir, tmpBase
+
+def getDefaultSubstitutions(test, tmpDir, tmpBase, normalize_slashes=False):
+ sourcepath = test.getSourcePath()
+ sourcedir = os.path.dirname(sourcepath)
# Normalize slashes, if requested.
if normalize_slashes:
@@ -417,7 +545,7 @@ def parseIntegratedTestScript(test, normalize_slashes=False,
tmpBase = tmpBase.replace('\\', '/')
# We use #_MARKER_# to hide %% while we do the other substitutions.
- substitutions = list(extra_substitutions)
+ substitutions = []
substitutions.extend([('%%', '#_MARKER_#')])
substitutions.extend(test.config.substitutions)
substitutions.extend([('%s', sourcepath),
@@ -436,13 +564,40 @@ def parseIntegratedTestScript(test, normalize_slashes=False,
('%/t', tmpBase.replace('\\', '/') + '.tmp'),
('%/T', tmpDir.replace('\\', '/')),
])
+ return substitutions
+
+def applySubstitutions(script, substitutions):
+ """Apply substitutions to the script. Allow full regular expression syntax.
+ Replace each matching occurrence of regular expression pattern a with
+ substitution b in line ln."""
+ def processLine(ln):
+ # Apply substitutions
+ for a,b in substitutions:
+ if kIsWindows:
+ b = b.replace("\\","\\\\")
+ ln = re.sub(a, b, ln)
+ # Strip the trailing newline and any extra whitespace.
+ return ln.strip()
+ # Note Python 3 map() gives an iterator rather than a list so explicitly
+ # convert to list before returning.
+ return list(map(processLine, script))
+
+def parseIntegratedTestScript(test, require_script=True):
+ """parseIntegratedTestScript - Scan an LLVM/Clang style integrated test
+ script and extract the lines to 'RUN' as well as 'XFAIL' and 'REQUIRES'
+ and 'UNSUPPORTED' information. If 'require_script' is False an empty script
+ may be returned. This can be used for test formats where the actual script
+ is optional or ignored.
+ """
# Collect the test lines from the script.
+ sourcepath = test.getSourcePath()
script = []
requires = []
unsupported = []
+ keywords = ['RUN:', 'XFAIL:', 'REQUIRES:', 'UNSUPPORTED:', 'END.']
for line_number, command_type, ln in \
- parseIntegratedTestScriptCommands(sourcepath):
+ parseIntegratedTestScriptCommands(sourcepath, keywords):
if command_type == 'RUN':
# Trim trailing whitespace.
ln = ln.rstrip()
@@ -475,21 +630,6 @@ def parseIntegratedTestScript(test, normalize_slashes=False,
raise ValueError("unknown script command type: %r" % (
command_type,))
- # Apply substitutions to the script. Allow full regular
- # expression syntax. Replace each matching occurrence of regular
- # expression pattern a with substitution b in line ln.
- def processLine(ln):
- # Apply substitutions
- for a,b in substitutions:
- if kIsWindows:
- b = b.replace("\\","\\\\")
- ln = re.sub(a, b, ln)
-
- # Strip the trailing newline and any extra whitespace.
- return ln.strip()
- script = [processLine(ln)
- for ln in script]
-
# Verify the script contains a run line.
if require_script and not script:
return lit.Test.Result(Test.UNRESOLVED, "Test has no run line!")
@@ -513,6 +653,13 @@ def parseIntegratedTestScript(test, normalize_slashes=False,
return lit.Test.Result(Test.UNSUPPORTED,
"Test is unsupported with the following features: %s" % msg)
+ unsupported_targets = [f for f in unsupported
+ if f in test.suite.config.target_triple]
+ if unsupported_targets:
+ return lit.Test.Result(Test.UNSUPPORTED,
+ "Test is unsupported with the following triple: %s" % (
+ test.suite.config.target_triple,))
+
if test.config.limit_to_features:
# Check that we have one of the limit_to_features features in requires.
limit_to_features_tests = [f for f in test.config.limit_to_features
@@ -522,13 +669,13 @@ def parseIntegratedTestScript(test, normalize_slashes=False,
return lit.Test.Result(Test.UNSUPPORTED,
"Test requires one of the limit_to_features features %s" % msg)
- return script,tmpBase,execdir
+ return script
-def _runShTest(test, litConfig, useExternalSh,
- script, tmpBase, execdir):
+def _runShTest(test, litConfig, useExternalSh, script, tmpBase):
# Create the output directory if it does not already exist.
lit.util.mkdir_p(os.path.dirname(tmpBase))
+ execdir = os.path.dirname(test.getExecPath())
if useExternalSh:
res = executeScript(test, litConfig, tmpBase, script, execdir)
else:
@@ -536,16 +683,23 @@ def _runShTest(test, litConfig, useExternalSh,
if isinstance(res, lit.Test.Result):
return res
- out,err,exitCode = res
+ out,err,exitCode,timeoutInfo = res
if exitCode == 0:
status = Test.PASS
else:
- status = Test.FAIL
+ if timeoutInfo == None:
+ status = Test.FAIL
+ else:
+ status = Test.TIMEOUT
# Form the output log.
- output = """Script:\n--\n%s\n--\nExit Code: %d\n\n""" % (
+ output = """Script:\n--\n%s\n--\nExit Code: %d\n""" % (
'\n'.join(script), exitCode)
+ if timeoutInfo != None:
+ output += """Timeout: %s\n""" % (timeoutInfo,)
+ output += "\n"
+
# Append the outputs, if present.
if out:
output += """Command Output (stdout):\n--\n%s\n--\n""" % (out,)
@@ -560,12 +714,28 @@ def executeShTest(test, litConfig, useExternalSh,
if test.config.unsupported:
return (Test.UNSUPPORTED, 'Test is unsupported')
- res = parseIntegratedTestScript(test, useExternalSh, extra_substitutions)
- if isinstance(res, lit.Test.Result):
- return res
+ script = parseIntegratedTestScript(test)
+ if isinstance(script, lit.Test.Result):
+ return script
if litConfig.noExecute:
return lit.Test.Result(Test.PASS)
- script, tmpBase, execdir = res
- return _runShTest(test, litConfig, useExternalSh, script, tmpBase, execdir)
+ tmpDir, tmpBase = getTempPaths(test)
+ substitutions = list(extra_substitutions)
+ substitutions += getDefaultSubstitutions(test, tmpDir, tmpBase,
+ normalize_slashes=useExternalSh)
+ script = applySubstitutions(script, substitutions)
+ # Re-run failed tests up to test_retry_attempts times.
+ attempts = 1
+ if hasattr(test.config, 'test_retry_attempts'):
+ attempts += test.config.test_retry_attempts
+ for i in range(attempts):
+ res = _runShTest(test, litConfig, useExternalSh, script, tmpBase)
+ if res.code != Test.FAIL:
+ break
+ # If we had to run the test more than once, count it as a flaky pass. These
+ # will be printed separately in the test summary.
+ if i > 0 and res.code == Test.PASS:
+ res.code = Test.FLAKYPASS
+ return res
diff --git a/utils/lit/lit/TestingConfig.py b/utils/lit/lit/TestingConfig.py
index d49d0c0767f24..7441b94b9f087 100644
--- a/utils/lit/lit/TestingConfig.py
+++ b/utils/lit/lit/TestingConfig.py
@@ -24,7 +24,7 @@ class TestingConfig:
pass_vars = ['LIBRARY_PATH', 'LD_LIBRARY_PATH', 'SYSTEMROOT', 'TERM',
'LD_PRELOAD', 'ASAN_OPTIONS', 'UBSAN_OPTIONS',
- 'LSAN_OPTIONS', 'ADB', 'ADB_SERIAL']
+ 'LSAN_OPTIONS', 'ADB', 'ANDROID_SERIAL']
for var in pass_vars:
val = os.environ.get(var, '')
# Check for empty string as some variables such as LD_PRELOAD cannot be empty
diff --git a/utils/lit/lit/formats/googletest.py b/utils/lit/lit/formats/googletest.py
index 3ce57917113aa..5b19d4e638f94 100644
--- a/utils/lit/lit/formats/googletest.py
+++ b/utils/lit/lit/formats/googletest.py
@@ -109,8 +109,15 @@ class GoogleTest(TestFormat):
if litConfig.noExecute:
return lit.Test.PASS, ''
- out, err, exitCode = lit.util.executeCommand(
- cmd, env=test.config.environment)
+ try:
+ out, err, exitCode = lit.util.executeCommand(
+ cmd, env=test.config.environment,
+ timeout=litConfig.maxIndividualTestTime)
+ except lit.util.ExecuteCommandTimeoutException:
+ return (lit.Test.TIMEOUT,
+ 'Reached timeout of {} seconds'.format(
+ litConfig.maxIndividualTestTime)
+ )
if exitCode:
return lit.Test.FAIL, out + err
diff --git a/utils/lit/lit/main.py b/utils/lit/lit/main.py
index e3722674f63f2..4df2571da998b 100755
--- a/utils/lit/lit/main.py
+++ b/utils/lit/lit/main.py
@@ -43,6 +43,7 @@ class TestingProgressDisplay(object):
test.getFullName())
shouldShow = test.result.code.isFailure or \
+ self.opts.showAllOutput or \
(not self.opts.quiet and not self.opts.succinct)
if not shouldShow:
return
@@ -56,9 +57,11 @@ class TestingProgressDisplay(object):
self.completed, self.numTests))
# Show the test failure output, if requested.
- if test.result.code.isFailure and self.opts.showOutput:
- print("%s TEST '%s' FAILED %s" % ('*'*20, test.getFullName(),
- '*'*20))
+ if (test.result.code.isFailure and self.opts.showOutput) or \
+ self.opts.showAllOutput:
+ if test.result.code.isFailure:
+ print("%s TEST '%s' FAILED %s" % ('*'*20, test.getFullName(),
+ '*'*20))
print(test.result.output)
print("*" * 20)
@@ -161,7 +164,10 @@ def main(builtinParameters = {}):
help="Reduce amount of output",
action="store_true", default=False)
group.add_option("-v", "--verbose", dest="showOutput",
- help="Show all test output",
+ help="Show test output for failures",
+ action="store_true", default=False)
+ group.add_option("-a", "--show-all", dest="showAllOutput",
+ help="Display all commandlines and output",
action="store_true", default=False)
group.add_option("-o", "--output", dest="output_path",
help="Write test results to the provided path",
@@ -199,6 +205,10 @@ def main(builtinParameters = {}):
group.add_option("", "--xunit-xml-output", dest="xunit_output_file",
help=("Write XUnit-compatible XML test reports to the"
" specified file"), default=None)
+ group.add_option("", "--timeout", dest="maxIndividualTestTime",
+ help="Maximum time to spend running a single test (in seconds)."
+ "0 means no time limit. [Default: 0]",
+ type=int, default=None)
parser.add_option_group(group)
group = OptionGroup(parser, "Test Selection")
@@ -269,6 +279,14 @@ def main(builtinParameters = {}):
name,val = entry.split('=', 1)
userParams[name] = val
+ # Decide what the requested maximum indvidual test time should be
+ if opts.maxIndividualTestTime != None:
+ maxIndividualTestTime = opts.maxIndividualTestTime
+ else:
+ # Default is zero
+ maxIndividualTestTime = 0
+
+
# Create the global config object.
litConfig = lit.LitConfig.LitConfig(
progname = os.path.basename(sys.argv[0]),
@@ -281,12 +299,26 @@ def main(builtinParameters = {}):
debug = opts.debug,
isWindows = isWindows,
params = userParams,
- config_prefix = opts.configPrefix)
+ config_prefix = opts.configPrefix,
+ maxIndividualTestTime = maxIndividualTestTime)
# Perform test discovery.
run = lit.run.Run(litConfig,
lit.discovery.find_tests_for_inputs(litConfig, inputs))
+ # After test discovery the configuration might have changed
+ # the maxIndividualTestTime. If we explicitly set this on the
+ # command line then override what was set in the test configuration
+ if opts.maxIndividualTestTime != None:
+ if opts.maxIndividualTestTime != litConfig.maxIndividualTestTime:
+ litConfig.note(('The test suite configuration requested an individual'
+ ' test timeout of {0} seconds but a timeout of {1} seconds was'
+ ' requested on the command line. Forcing timeout to be {1}'
+ ' seconds')
+ .format(litConfig.maxIndividualTestTime,
+ opts.maxIndividualTestTime))
+ litConfig.maxIndividualTestTime = opts.maxIndividualTestTime
+
if opts.showSuites or opts.showTests:
# Aggregate the tests by suite.
suitesAndTests = {}
@@ -344,12 +376,33 @@ def main(builtinParameters = {}):
# Don't create more threads than tests.
opts.numThreads = min(len(run.tests), opts.numThreads)
+ # Because some tests use threads internally, and at least on Linux each
+ # of these threads counts toward the current process limit, try to
+ # raise the (soft) process limit so that tests don't fail due to
+ # resource exhaustion.
+ try:
+ cpus = lit.util.detectCPUs()
+ desired_limit = opts.numThreads * cpus * 2 # the 2 is a safety factor
+
+ # Import the resource module here inside this try block because it
+ # will likely fail on Windows.
+ import resource
+
+ max_procs_soft, max_procs_hard = resource.getrlimit(resource.RLIMIT_NPROC)
+ desired_limit = min(desired_limit, max_procs_hard)
+
+ if max_procs_soft < desired_limit:
+ resource.setrlimit(resource.RLIMIT_NPROC, (desired_limit, max_procs_hard))
+ litConfig.note('raised the process limit from %d to %d' % \
+ (max_procs_soft, desired_limit))
+ except:
+ pass
+
extra = ''
if len(run.tests) != numTotalTests:
extra = ' of %d' % numTotalTests
header = '-- Testing: %d%s tests, %d threads --'%(len(run.tests), extra,
opts.numThreads)
-
progressBar = None
if not opts.quiet:
if opts.succinct and opts.useProgressBar:
@@ -394,7 +447,8 @@ def main(builtinParameters = {}):
('Failing Tests', lit.Test.FAIL),
('Unresolved Tests', lit.Test.UNRESOLVED),
('Unsupported Tests', lit.Test.UNSUPPORTED),
- ('Expected Failing Tests', lit.Test.XFAIL)):
+ ('Expected Failing Tests', lit.Test.XFAIL),
+ ('Timed Out Tests', lit.Test.TIMEOUT)):
if (lit.Test.XFAIL == code and not opts.show_xfail) or \
(lit.Test.UNSUPPORTED == code and not opts.show_unsupported):
continue
@@ -414,11 +468,13 @@ def main(builtinParameters = {}):
lit.util.printHistogram(test_times, title='Tests')
for name,code in (('Expected Passes ', lit.Test.PASS),
+ ('Passes With Retry ', lit.Test.FLAKYPASS),
('Expected Failures ', lit.Test.XFAIL),
('Unsupported Tests ', lit.Test.UNSUPPORTED),
('Unresolved Tests ', lit.Test.UNRESOLVED),
('Unexpected Passes ', lit.Test.XPASS),
- ('Unexpected Failures', lit.Test.FAIL)):
+ ('Unexpected Failures', lit.Test.FAIL),
+ ('Individual Timeouts', lit.Test.TIMEOUT)):
if opts.quiet and not code.isFailure:
continue
N = len(byCode.get(code,[]))
diff --git a/utils/lit/lit/util.py b/utils/lit/lit/util.py
index 08f7b71ae210b..a6e8d52c07549 100644
--- a/utils/lit/lit/util.py
+++ b/utils/lit/lit/util.py
@@ -6,6 +6,7 @@ import platform
import signal
import subprocess
import sys
+import threading
def to_bytes(str):
# Encode to UTF-8 to get binary data.
@@ -39,7 +40,9 @@ def detectCPUs():
if "NUMBER_OF_PROCESSORS" in os.environ:
ncpus = int(os.environ["NUMBER_OF_PROCESSORS"])
if ncpus > 0:
- return ncpus
+ # With more than 32 processes, process creation often fails with
+ # "Too many open files". FIXME: Check if there's a better fix.
+ return min(ncpus, 32)
return 1 # Default
def mkdir_p(path):
@@ -48,7 +51,7 @@ def mkdir_p(path):
if not path or os.path.exists(path):
return
- parent = os.path.dirname(path)
+ parent = os.path.dirname(path)
if parent != path:
mkdir_p(parent)
@@ -94,7 +97,7 @@ def which(command, paths = None):
for path in paths.split(os.pathsep):
for ext in pathext:
p = os.path.join(path, command + ext)
- if os.path.exists(p):
+ if os.path.exists(p) and not os.path.isdir(p):
return p
return None
@@ -155,26 +158,83 @@ def printHistogram(items, title = 'Items'):
pDigits, pfDigits, i*barH, pDigits, pfDigits, (i+1)*barH,
'*'*w, ' '*(barW-w), cDigits, len(row), cDigits, len(items)))
+class ExecuteCommandTimeoutException(Exception):
+ def __init__(self, msg, out, err, exitCode):
+ assert isinstance(msg, str)
+ assert isinstance(out, str)
+ assert isinstance(err, str)
+ assert isinstance(exitCode, int)
+ self.msg = msg
+ self.out = out
+ self.err = err
+ self.exitCode = exitCode
+
# Close extra file handles on UNIX (on Windows this cannot be done while
# also redirecting input).
kUseCloseFDs = not (platform.system() == 'Windows')
-def executeCommand(command, cwd=None, env=None):
+def executeCommand(command, cwd=None, env=None, input=None, timeout=0):
+ """
+ Execute command ``command`` (list of arguments or string)
+ with
+ * working directory ``cwd`` (str), use None to use the current
+ working directory
+ * environment ``env`` (dict), use None for none
+ * Input to the command ``input`` (str), use string to pass
+ no input.
+ * Max execution time ``timeout`` (int) seconds. Use 0 for no timeout.
+
+ Returns a tuple (out, err, exitCode) where
+ * ``out`` (str) is the standard output of running the command
+ * ``err`` (str) is the standard error of running the command
+ * ``exitCode`` (int) is the exitCode of running the command
+
+ If the timeout is hit an ``ExecuteCommandTimeoutException``
+ is raised.
+ """
p = subprocess.Popen(command, cwd=cwd,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=env, close_fds=kUseCloseFDs)
- out,err = p.communicate()
- exitCode = p.wait()
+ timerObject = None
+ # FIXME: Because of the way nested function scopes work in Python 2.x we
+ # need to use a reference to a mutable object rather than a plain
+ # bool. In Python 3 we could use the "nonlocal" keyword but we need
+ # to support Python 2 as well.
+ hitTimeOut = [False]
+ try:
+ if timeout > 0:
+ def killProcess():
+ # We may be invoking a shell so we need to kill the
+ # process and all its children.
+ hitTimeOut[0] = True
+ killProcessAndChildren(p.pid)
- # Detect Ctrl-C in subprocess.
- if exitCode == -signal.SIGINT:
- raise KeyboardInterrupt
+ timerObject = threading.Timer(timeout, killProcess)
+ timerObject.start()
+
+ out,err = p.communicate(input=input)
+ exitCode = p.wait()
+ finally:
+ if timerObject != None:
+ timerObject.cancel()
# Ensure the resulting output is always of string type.
out = convert_string(out)
err = convert_string(err)
+ if hitTimeOut[0]:
+ raise ExecuteCommandTimeoutException(
+ msg='Reached timeout of {} seconds'.format(timeout),
+ out=out,
+ err=err,
+ exitCode=exitCode
+ )
+
+ # Detect Ctrl-C in subprocess.
+ if exitCode == -signal.SIGINT:
+ raise KeyboardInterrupt
+
return out, err, exitCode
def usePlatformSdkOnDarwin(config, lit_config):
@@ -193,3 +253,25 @@ def usePlatformSdkOnDarwin(config, lit_config):
sdk_path = out
lit_config.note('using SDKROOT: %r' % sdk_path)
config.environment['SDKROOT'] = sdk_path
+
+def killProcessAndChildren(pid):
+ """
+ This function kills a process with ``pid`` and all its
+ running children (recursively). It is currently implemented
+ using the psutil module which provides a simple platform
+ neutral implementation.
+
+ TODO: Reimplement this without using psutil so we can
+ remove our dependency on it.
+ """
+ import psutil
+ try:
+ psutilProc = psutil.Process(pid)
+ for child in psutilProc.children(recursive=True):
+ try:
+ child.kill()
+ except psutil.NoSuchProcess:
+ pass
+ psutilProc.kill()
+ except psutil.NoSuchProcess:
+ pass
diff --git a/utils/lit/tests/Inputs/googletest-timeout/DummySubDir/OneTest b/utils/lit/tests/Inputs/googletest-timeout/DummySubDir/OneTest
new file mode 100755
index 0000000000000..f3a90ff4cd675
--- /dev/null
+++ b/utils/lit/tests/Inputs/googletest-timeout/DummySubDir/OneTest
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+
+import sys
+import time
+
+if len(sys.argv) != 2:
+ raise ValueError("unexpected number of args")
+
+if sys.argv[1] == "--gtest_list_tests":
+ print("""\
+FirstTest.
+ subTestA
+ subTestB
+ subTestC
+""")
+ sys.exit(0)
+elif not sys.argv[1].startswith("--gtest_filter="):
+ raise ValueError("unexpected argument: %r" % (sys.argv[1]))
+
+test_name = sys.argv[1].split('=',1)[1]
+if test_name == 'FirstTest.subTestA':
+ print('I am subTest A, I PASS')
+ print('[ PASSED ] 1 test.')
+ sys.exit(0)
+elif test_name == 'FirstTest.subTestB':
+ print('I am subTest B, I am slow')
+ time.sleep(6)
+ print('[ PASSED ] 1 test.')
+ sys.exit(0)
+elif test_name == 'FirstTest.subTestC':
+ print('I am subTest C, I will hang')
+ while True:
+ pass
+else:
+ raise SystemExit("error: invalid test name: %r" % (test_name,))
diff --git a/utils/lit/tests/Inputs/googletest-timeout/lit.cfg b/utils/lit/tests/Inputs/googletest-timeout/lit.cfg
new file mode 100644
index 0000000000000..bf8a4db2bf90e
--- /dev/null
+++ b/utils/lit/tests/Inputs/googletest-timeout/lit.cfg
@@ -0,0 +1,9 @@
+import lit.formats
+config.name = 'googletest-timeout'
+config.test_format = lit.formats.GoogleTest('DummySubDir', 'Test')
+
+configSetTimeout = lit_config.params.get('set_timeout', '0')
+
+if configSetTimeout == '1':
+ # Try setting the max individual test time in the configuration
+ lit_config.maxIndividualTestTime = 1
diff --git a/utils/lit/tests/Inputs/shtest-timeout/infinite_loop.py b/utils/lit/tests/Inputs/shtest-timeout/infinite_loop.py
new file mode 100644
index 0000000000000..55720479d3331
--- /dev/null
+++ b/utils/lit/tests/Inputs/shtest-timeout/infinite_loop.py
@@ -0,0 +1,10 @@
+# RUN: %{python} %s
+from __future__ import print_function
+
+import time
+import sys
+
+print("Running infinite loop")
+sys.stdout.flush() # Make sure the print gets flushed so it appears in lit output.
+while True:
+ pass
diff --git a/utils/lit/tests/Inputs/shtest-timeout/lit.cfg b/utils/lit/tests/Inputs/shtest-timeout/lit.cfg
new file mode 100644
index 0000000000000..81b4a12120d88
--- /dev/null
+++ b/utils/lit/tests/Inputs/shtest-timeout/lit.cfg
@@ -0,0 +1,32 @@
+# -*- Python -*-
+import os
+import sys
+
+import lit.formats
+
+config.name = 'per_test_timeout'
+
+shellType = lit_config.params.get('external', '1')
+
+if shellType == '0':
+ lit_config.note('Using internal shell')
+ externalShell = False
+else:
+ lit_config.note('Using external shell')
+ externalShell = True
+
+configSetTimeout = lit_config.params.get('set_timeout', '0')
+
+if configSetTimeout == '1':
+ # Try setting the max individual test time in the configuration
+ lit_config.maxIndividualTestTime = 1
+
+config.test_format = lit.formats.ShTest(execute_external=externalShell)
+config.suffixes = ['.py']
+
+config.test_source_root = os.path.dirname(__file__)
+config.test_exec_root = config.test_source_root
+config.target_triple = '(unused)'
+src_root = os.path.join(config.test_source_root, '..')
+config.environment['PYTHONPATH'] = src_root
+config.substitutions.append(('%{python}', sys.executable))
diff --git a/utils/lit/tests/Inputs/shtest-timeout/quick_then_slow.py b/utils/lit/tests/Inputs/shtest-timeout/quick_then_slow.py
new file mode 100644
index 0000000000000..b81fbe5a8bfe5
--- /dev/null
+++ b/utils/lit/tests/Inputs/shtest-timeout/quick_then_slow.py
@@ -0,0 +1,24 @@
+# RUN: %{python} %s quick
+# RUN: %{python} %s slow
+from __future__ import print_function
+
+import time
+import sys
+
+if len(sys.argv) != 2:
+ print("Wrong number of args")
+ sys.exit(1)
+
+mode = sys.argv[1]
+
+if mode == 'slow':
+ print("Running in slow mode")
+ sys.stdout.flush() # Make sure the print gets flushed so it appears in lit output.
+ time.sleep(6)
+ sys.exit(0)
+elif mode == 'quick':
+ print("Running in quick mode")
+ sys.exit(0)
+else:
+ print("Unrecognised mode {}".format(mode))
+ sys.exit(1)
diff --git a/utils/lit/tests/Inputs/shtest-timeout/short.py b/utils/lit/tests/Inputs/shtest-timeout/short.py
new file mode 100644
index 0000000000000..424b7092d8392
--- /dev/null
+++ b/utils/lit/tests/Inputs/shtest-timeout/short.py
@@ -0,0 +1,6 @@
+# RUN: %{python} %s
+from __future__ import print_function
+
+import sys
+
+print("short program")
diff --git a/utils/lit/tests/Inputs/shtest-timeout/slow.py b/utils/lit/tests/Inputs/shtest-timeout/slow.py
new file mode 100644
index 0000000000000..2dccd6331360b
--- /dev/null
+++ b/utils/lit/tests/Inputs/shtest-timeout/slow.py
@@ -0,0 +1,9 @@
+# RUN: %{python} %s
+from __future__ import print_function
+
+import time
+import sys
+
+print("Running slow program")
+sys.stdout.flush() # Make sure the print gets flushed so it appears in lit output.
+time.sleep(6)
diff --git a/utils/lit/tests/discovery.py b/utils/lit/tests/discovery.py
index 60f67dcd740e3..55e54088b587b 100644
--- a/utils/lit/tests/discovery.py
+++ b/utils/lit/tests/discovery.py
@@ -7,8 +7,8 @@
# RUN: FileCheck --check-prefix=CHECK-BASIC-ERR < %t.err %s
#
# CHECK-BASIC-ERR: loading suite config '{{.*}}/discovery/lit.cfg'
-# CHECK-BASIC-ERR: loading suite config '{{.*}}/discovery/subsuite/lit.cfg'
-# CHECK-BASIC-ERR: loading local config '{{.*}}/discovery/subdir/lit.local.cfg'
+# CHECK-BASIC-ERR-DAG: loading suite config '{{.*}}/discovery/subsuite/lit.cfg'
+# CHECK-BASIC-ERR-DAG: loading local config '{{.*}}/discovery/subdir/lit.local.cfg'
#
# CHECK-BASIC-OUT: -- Test Suites --
# CHECK-BASIC-OUT: sub-suite - 2 tests
@@ -51,8 +51,8 @@
# CHECK-ASEXEC-ERR: load_config from '{{.*}}/discovery/lit.cfg'
# CHECK-ASEXEC-ERR: loaded config '{{.*}}/discovery/lit.cfg'
# CHECK-ASEXEC-ERR: loaded config '{{.*}}/exec-discovery/lit.site.cfg'
-# CHECK-ASEXEC-ERR: loading suite config '{{.*}}/discovery/subsuite/lit.cfg'
-# CHECK-ASEXEC-ERR: loading local config '{{.*}}/discovery/subdir/lit.local.cfg'
+# CHECK-ASEXEC-ERR-DAG: loading suite config '{{.*}}/discovery/subsuite/lit.cfg'
+# CHECK-ASEXEC-ERR-DAG: loading local config '{{.*}}/discovery/subdir/lit.local.cfg'
#
# CHECK-ASEXEC-OUT: -- Test Suites --
# CHECK-ASEXEC-OUT: sub-suite - 2 tests
diff --git a/utils/lit/tests/googletest-timeout.py b/utils/lit/tests/googletest-timeout.py
new file mode 100644
index 0000000000000..46acf32b3a616
--- /dev/null
+++ b/utils/lit/tests/googletest-timeout.py
@@ -0,0 +1,29 @@
+# REQUIRES: python-psutil
+
+# Check that the per test timeout is enforced when running GTest tests.
+#
+# RUN: not %{lit} -j 1 -v %{inputs}/googletest-timeout --timeout=1 > %t.cmd.out
+# RUN: FileCheck < %t.cmd.out %s
+
+# Check that the per test timeout is enforced when running GTest tests via
+# the configuration file
+#
+# RUN: not %{lit} -j 1 -v %{inputs}/googletest-timeout \
+# RUN: --param set_timeout=1 > %t.cfgset.out 2> %t.cfgset.err
+# RUN: FileCheck < %t.cfgset.out %s
+
+# CHECK: -- Testing:
+# CHECK: PASS: googletest-timeout :: DummySubDir/OneTest/FirstTest.subTestA
+# CHECK: TIMEOUT: googletest-timeout :: DummySubDir/OneTest/FirstTest.subTestB
+# CHECK: TIMEOUT: googletest-timeout :: DummySubDir/OneTest/FirstTest.subTestC
+# CHECK: Expected Passes : 1
+# CHECK: Individual Timeouts: 2
+
+# Test per test timeout via a config file and on the command line.
+# The value set on the command line should override the config file.
+# RUN: not %{lit} -j 1 -v %{inputs}/googletest-timeout \
+# RUN: --param set_timeout=1 --timeout=2 > %t.cmdover.out 2> %t.cmdover.err
+# RUN: FileCheck < %t.cmdover.out %s
+# RUN: FileCheck --check-prefix=CHECK-CMDLINE-OVERRIDE-ERR < %t.cmdover.err %s
+
+# CHECK-CMDLINE-OVERRIDE-ERR: Forcing timeout to be 2 seconds
diff --git a/utils/lit/tests/lit.cfg b/utils/lit/tests/lit.cfg
index 2111b72748b59..4b38241d5a7d3 100644
--- a/utils/lit/tests/lit.cfg
+++ b/utils/lit/tests/lit.cfg
@@ -43,3 +43,12 @@ if lit_config.params.get('check-coverage', None):
# Add a feature to detect the Python version.
config.available_features.add("python%d.%d" % (sys.version_info[0],
sys.version_info[1]))
+
+# Add a feature to detect if psutil is available
+try:
+ import psutil
+ lit_config.note('Found python psutil module')
+ config.available_features.add("python-psutil")
+except ImportError:
+ lit_config.warning('Could not import psutil. Some tests will be skipped and'
+ ' the --timeout command line argument will not work.')
diff --git a/utils/lit/tests/shtest-timeout.py b/utils/lit/tests/shtest-timeout.py
new file mode 100644
index 0000000000000..e6b2947a4f7f2
--- /dev/null
+++ b/utils/lit/tests/shtest-timeout.py
@@ -0,0 +1,116 @@
+# REQUIRES: python-psutil
+
+# Test per test timeout using external shell
+# RUN: not %{lit} \
+# RUN: %{inputs}/shtest-timeout/infinite_loop.py \
+# RUN: %{inputs}/shtest-timeout/quick_then_slow.py \
+# RUN: %{inputs}/shtest-timeout/short.py \
+# RUN: %{inputs}/shtest-timeout/slow.py \
+# RUN: -j 1 -v --debug --timeout 1 --param external=1 > %t.extsh.out 2> %t.extsh.err
+# RUN: FileCheck --check-prefix=CHECK-OUT-COMMON < %t.extsh.out %s
+# RUN: FileCheck --check-prefix=CHECK-EXTSH-ERR < %t.extsh.err %s
+#
+# CHECK-EXTSH-ERR: Using external shell
+
+# Test per test timeout using internal shell
+# RUN: not %{lit} \
+# RUN: %{inputs}/shtest-timeout/infinite_loop.py \
+# RUN: %{inputs}/shtest-timeout/quick_then_slow.py \
+# RUN: %{inputs}/shtest-timeout/short.py \
+# RUN: %{inputs}/shtest-timeout/slow.py \
+# RUN: -j 1 -v --debug --timeout 1 --param external=0 > %t.intsh.out 2> %t.intsh.err
+# RUN: FileCheck --check-prefix=CHECK-OUT-COMMON < %t.intsh.out %s
+# RUN: FileCheck --check-prefix=CHECK-INTSH-OUT < %t.intsh.out %s
+# RUN: FileCheck --check-prefix=CHECK-INTSH-ERR < %t.intsh.err %s
+#
+# CHECK-INTSH-OUT: TIMEOUT: per_test_timeout :: infinite_loop.py
+# CHECK-INTSH-OUT: Command 0 Reached Timeout: True
+# CHECK-INTSH-OUT: Command 0 Output:
+# CHECK-INTSH-OUT-NEXT: Running infinite loop
+
+
+# CHECK-INTSH-OUT: TIMEOUT: per_test_timeout :: quick_then_slow.py
+# CHECK-INTSH-OUT: Timeout: Reached timeout of 1 seconds
+# CHECK-INTSH-OUT: Command Output
+# CHECK-INTSH-OUT: Command 0 Reached Timeout: False
+# CHECK-INTSH-OUT: Command 0 Output:
+# CHECK-INTSH-OUT-NEXT: Running in quick mode
+# CHECK-INTSH-OUT: Command 1 Reached Timeout: True
+# CHECK-INTSH-OUT: Command 1 Output:
+# CHECK-INTSH-OUT-NEXT: Running in slow mode
+
+# CHECK-INTSH-OUT: TIMEOUT: per_test_timeout :: slow.py
+# CHECK-INTSH-OUT: Command 0 Reached Timeout: True
+# CHECK-INTSH-OUT: Command 0 Output:
+# CHECK-INTSH-OUT-NEXT: Running slow program
+
+# CHECK-INTSH-ERR: Using internal shell
+
+# Test per test timeout set via a config file rather than on the command line
+# RUN: not %{lit} \
+# RUN: %{inputs}/shtest-timeout/infinite_loop.py \
+# RUN: %{inputs}/shtest-timeout/quick_then_slow.py \
+# RUN: %{inputs}/shtest-timeout/short.py \
+# RUN: %{inputs}/shtest-timeout/slow.py \
+# RUN: -j 1 -v --debug --param external=0 \
+# RUN: --param set_timeout=1 > %t.cfgset.out 2> %t.cfgset.err
+# RUN: FileCheck --check-prefix=CHECK-OUT-COMMON < %t.cfgset.out %s
+# RUN: FileCheck --check-prefix=CHECK-CFGSET-ERR < %t.cfgset.err %s
+#
+# CHECK-CFGSET-ERR: Using internal shell
+
+# CHECK-OUT-COMMON: TIMEOUT: per_test_timeout :: infinite_loop.py
+# CHECK-OUT-COMMON: Timeout: Reached timeout of 1 seconds
+# CHECK-OUT-COMMON: Command {{([0-9]+ )?}}Output
+# CHECK-OUT-COMMON: Running infinite loop
+
+# CHECK-OUT-COMMON: TIMEOUT: per_test_timeout :: quick_then_slow.py
+# CHECK-OUT-COMMON: Timeout: Reached timeout of 1 seconds
+# CHECK-OUT-COMMON: Command {{([0-9]+ )?}}Output
+# CHECK-OUT-COMMON: Running in quick mode
+# CHECK-OUT-COMMON: Running in slow mode
+
+# CHECK-OUT-COMMON: PASS: per_test_timeout :: short.py
+
+# CHECK-OUT-COMMON: TIMEOUT: per_test_timeout :: slow.py
+# CHECK-OUT-COMMON: Timeout: Reached timeout of 1 seconds
+# CHECK-OUT-COMMON: Command {{([0-9]+ )?}}Output
+# CHECK-OUT-COMMON: Running slow program
+
+# CHECK-OUT-COMMON: Expected Passes{{ *}}: 1
+# CHECK-OUT-COMMON: Individual Timeouts{{ *}}: 3
+
+# Test per test timeout via a config file and on the command line.
+# The value set on the command line should override the config file.
+# RUN: not %{lit} \
+# RUN: %{inputs}/shtest-timeout/infinite_loop.py \
+# RUN: %{inputs}/shtest-timeout/quick_then_slow.py \
+# RUN: %{inputs}/shtest-timeout/short.py \
+# RUN: %{inputs}/shtest-timeout/slow.py \
+# RUN: -j 1 -v --debug --param external=0 \
+# RUN: --param set_timeout=1 --timeout=2 > %t.cmdover.out 2> %t.cmdover.err
+# RUN: FileCheck --check-prefix=CHECK-CMDLINE-OVERRIDE-OUT < %t.cmdover.out %s
+# RUN: FileCheck --check-prefix=CHECK-CMDLINE-OVERRIDE-ERR < %t.cmdover.err %s
+
+# CHECK-CMDLINE-OVERRIDE-ERR: Forcing timeout to be 2 seconds
+
+# CHECK-CMDLINE-OVERRIDE-OUT: TIMEOUT: per_test_timeout :: infinite_loop.py
+# CHECK-CMDLINE-OVERRIDE-OUT: Timeout: Reached timeout of 2 seconds
+# CHECK-CMDLINE-OVERRIDE-OUT: Command {{([0-9]+ )?}}Output
+# CHECK-CMDLINE-OVERRIDE-OUT: Running infinite loop
+
+# CHECK-CMDLINE-OVERRIDE-OUT: TIMEOUT: per_test_timeout :: quick_then_slow.py
+# CHECK-CMDLINE-OVERRIDE-OUT: Timeout: Reached timeout of 2 seconds
+# CHECK-CMDLINE-OVERRIDE-OUT: Command {{([0-9]+ )?}}Output
+# CHECK-CMDLINE-OVERRIDE-OUT: Running in quick mode
+# CHECK-CMDLINE-OVERRIDE-OUT: Running in slow mode
+
+# CHECK-CMDLINE-OVERRIDE-OUT: PASS: per_test_timeout :: short.py
+
+# CHECK-CMDLINE-OVERRIDE-OUT: TIMEOUT: per_test_timeout :: slow.py
+# CHECK-CMDLINE-OVERRIDE-OUT: Timeout: Reached timeout of 2 seconds
+# CHECK-CMDLINE-OVERRIDE-OUT: Command {{([0-9]+ )?}}Output
+# CHECK-CMDLINE-OVERRIDE-OUT: Running slow program
+
+# CHECK-CMDLINE-OVERRIDE-OUT: Expected Passes{{ *}}: 1
+# CHECK-CMDLINE-OVERRIDE-OUT: Individual Timeouts{{ *}}: 3
diff --git a/utils/llvm-build/llvmbuild/main.py b/utils/llvm-build/llvmbuild/main.py
index 353741fdbf855..f2472f698a671 100644
--- a/utils/llvm-build/llvmbuild/main.py
+++ b/utils/llvm-build/llvmbuild/main.py
@@ -411,11 +411,15 @@ subdirectories = %s
f.write('} AvailableComponents[%d] = {\n' % len(entries))
for name,library_name,required_names,is_installed in entries:
if library_name is None:
- library_name_as_cstr = '0'
+ library_name_as_cstr = 'nullptr'
else:
library_name_as_cstr = '"lib%s.a"' % library_name
- f.write(' { "%s", %s, %d, { %s } },\n' % (
- name, library_name_as_cstr, is_installed,
+ if is_installed:
+ is_installed_as_cstr = 'true'
+ else:
+ is_installed_as_cstr = 'false'
+ f.write(' { "%s", %s, %s, { %s } },\n' % (
+ name, library_name_as_cstr, is_installed_as_cstr,
', '.join('"%s"' % dep
for dep in required_names)))
f.write('};\n')
@@ -501,6 +505,33 @@ subdirectories = %s
if (path.startswith(self.source_root) and os.path.exists(path)):
yield path
+ def foreach_cmake_library(self, f,
+ enabled_optional_components,
+ skip_disabled,
+ skip_not_installed):
+ for ci in self.ordered_component_infos:
+ # Skip optional components which are not enabled.
+ if ci.type_name == 'OptionalLibrary' \
+ and ci.name not in enabled_optional_components:
+ continue
+
+ # We only write the information for libraries currently.
+ if ci.type_name not in ('Library', 'OptionalLibrary'):
+ continue
+
+ # Skip disabled targets.
+ if skip_disabled:
+ tg = ci.get_parent_target_group()
+ if tg and not tg.enabled:
+ continue
+
+ # Skip targets that will not be installed
+ if skip_not_installed and not ci.installed:
+ continue
+
+ f(ci)
+
+
def write_cmake_fragment(self, output_path, enabled_optional_components):
"""
write_cmake_fragment(output_path) -> None
@@ -569,21 +600,18 @@ configure_file(\"%s\"
# The following property assignments effectively create a map from component
# names to required libraries, in a way that is easily accessed from CMake.
""")
- for ci in self.ordered_component_infos:
- # Skip optional components which are not enabled.
- if ci.type_name == 'OptionalLibrary' \
- and ci.name not in enabled_optional_components:
- continue
-
- # We only write the information for certain components currently.
- if ci.type_name not in ('Library', 'OptionalLibrary'):
- continue
-
- f.write("""\
+ self.foreach_cmake_library(
+ lambda ci:
+ f.write("""\
set_property(GLOBAL PROPERTY LLVMBUILD_LIB_DEPS_%s %s)\n""" % (
ci.get_prefixed_library_name(), " ".join(sorted(
dep.get_prefixed_library_name()
for dep in self.get_required_libraries_for_component(ci)))))
+ ,
+ enabled_optional_components,
+ skip_disabled = False,
+ skip_not_installed = False # Dependency info must be emitted for internals libs too
+ )
f.close()
@@ -608,30 +636,22 @@ set_property(GLOBAL PROPERTY LLVMBUILD_LIB_DEPS_%s %s)\n""" % (
# The following property assignments tell CMake about link
# dependencies of libraries imported from LLVM.
""")
- for ci in self.ordered_component_infos:
- # Skip optional components which are not enabled.
- if ci.type_name == 'OptionalLibrary' \
- and ci.name not in enabled_optional_components:
- continue
-
- # We only write the information for libraries currently.
- if ci.type_name not in ('Library', 'OptionalLibrary'):
- continue
-
- # Skip disabled targets.
- tg = ci.get_parent_target_group()
- if tg and not tg.enabled:
- continue
-
- f.write("""\
+ self.foreach_cmake_library(
+ lambda ci:
+ f.write("""\
set_property(TARGET %s PROPERTY IMPORTED_LINK_INTERFACE_LIBRARIES %s)\n""" % (
ci.get_prefixed_library_name(), " ".join(sorted(
dep.get_prefixed_library_name()
for dep in self.get_required_libraries_for_component(ci)))))
+ ,
+ enabled_optional_components,
+ skip_disabled = True,
+ skip_not_installed = True # Do not export internal libraries like gtest
+ )
f.close()
- def write_make_fragment(self, output_path):
+ def write_make_fragment(self, output_path, enabled_optional_components):
"""
write_make_fragment(output_path) -> None
@@ -697,6 +717,19 @@ set_property(TARGET %s PROPERTY IMPORTED_LINK_INTERFACE_LIBRARIES %s)\n""" % (
f.write("%s:\n" % (mk_quote_string_for_target(dep),))
f.write('endif\n')
+ f.write("""
+# List of libraries to be exported for use by applications.
+# See 'cmake/modules/Makefile'.
+LLVM_LIBS_TO_EXPORT :=""")
+ self.foreach_cmake_library(
+ lambda ci:
+ f.write(' \\\n %s' % ci.get_prefixed_library_name())
+ ,
+ enabled_optional_components,
+ skip_disabled = True,
+ skip_not_installed = True # Do not export internal libraries like gtest
+ )
+ f.write('\n')
f.close()
def add_magic_target_components(parser, project, opts):
@@ -920,7 +953,8 @@ given by --build-root) at the same SUBPATH""",
# Write out the make fragment, if requested.
if opts.write_make_fragment:
- project_info.write_make_fragment(opts.write_make_fragment)
+ project_info.write_make_fragment(opts.write_make_fragment,
+ opts.optional_components)
# Write out the cmake fragment, if requested.
if opts.write_cmake_fragment:
diff --git a/utils/release/build_llvm_package.bat b/utils/release/build_llvm_package.bat
new file mode 100755
index 0000000000000..830f25e5cf389
--- /dev/null
+++ b/utils/release/build_llvm_package.bat
@@ -0,0 +1,93 @@
+@echo off
+setlocal
+
+REM Script for building the LLVM installer on Windows,
+REM used for the the weekly snapshots at http://www.llvm.org/builds.
+REM
+REM Usage: build_llvm_package.bat <revision>
+
+REM Prerequisites:
+REM
+REM Visual Studio 2013, CMake, Ninja, SVN, GNUWin32,
+REM NSIS with the strlen_8192 patch,
+REM Visual Studio 2013 SDK (for the clang-format plugin).
+
+
+REM You may need to modify the paths below:
+set vcdir=c:\Program Files (x86)\Microsoft Visual Studio 12.0\VC
+set PATH=%PATH%;c:\gnuwin32\bin
+
+set revision=%1
+set branch=trunk
+set package_version=3.8.0-r%revision%
+set clang_format_vs_version=3.8.0.%revision%
+set build_dir=llvm_package_%revision%
+
+echo Branch: %branch%
+echo Revision: %revision%
+echo Package version: %package_version%
+echo Clang format plugin version: %clang_format_vs_version%
+echo Build dir: %build_dir%
+echo.
+pause
+
+mkdir %build_dir%
+cd %build_dir%
+
+echo Checking out %branch% at r%revision%...
+svn.exe export -r %revision% http://llvm.org/svn/llvm-project/llvm/%branch% llvm || exit /b
+svn.exe export -r %revision% http://llvm.org/svn/llvm-project/cfe/%branch% llvm/tools/clang || exit /b
+svn.exe export -r %revision% http://llvm.org/svn/llvm-project/clang-tools-extra/%branch% llvm/tools/clang/tools/extra || exit /b
+svn.exe export -r %revision% http://llvm.org/svn/llvm-project/lld/%branch% llvm/tools/lld || exit /b
+svn.exe export -r %revision% http://llvm.org/svn/llvm-project/compiler-rt/%branch% llvm/projects/compiler-rt || exit /b
+
+
+set cmake_flags=-DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON -DLLVM_INSTALL_TOOLCHAIN_ONLY=ON -DLLVM_USE_CRT_RELEASE=MT -DCLANG_FORMAT_VS_VERSION=%clang_format_vs_version% -DPACKAGE_VERSION=%package_version%
+
+REM TODO: Run all tests, including lld and compiler-rt.
+
+call "%vcdir%/vcvarsall.bat" x86
+set CC=
+set CXX=
+mkdir build32_stage0
+cd build32_stage0
+cmake -GNinja %cmake_flags% ..\llvm || exit /b
+ninja all || exit /b
+ninja check || exit /b
+ninja check-clang || exit /b
+cd..
+
+mkdir build32
+cd build32
+set CC=..\build32_stage0\bin\clang-cl
+set CXX=..\build32_stage0\bin\clang-cl
+cmake -GNinja %cmake_flags% -DBUILD_CLANG_FORMAT_VS_PLUGIN=ON ..\llvm || exit /b
+ninja all || exit /b
+ninja check || exit /b
+ninja check-clang || exit /b
+copy ..\llvm\tools\clang\tools\clang-format-vs\ClangFormat\bin\Release\ClangFormat.vsix ClangFormat-r%revision%.vsix
+ninja package || exit /b
+cd ..
+
+
+call "%vcdir%/vcvarsall.bat" amd64
+set CC=
+set CXX=
+mkdir build64_stage0
+cd build64_stage0
+cmake -GNinja %cmake_flags% ..\llvm || exit /b
+ninja all || exit /b
+ninja check || exit /b
+ninja check-clang || exit /b
+cd..
+
+mkdir build64
+cd build64
+set CC=..\build64_stage0\bin\clang-cl
+set CXX=..\build64_stage0\bin\clang-cl
+cmake -GNinja %cmake_flags% ..\llvm || exit /b
+ninja all || exit /b
+ninja check || exit /b
+ninja check-clang || exit /b
+ninja package || exit /b
+cd ..
diff --git a/utils/release/merge.sh b/utils/release/merge.sh
index 949c298378134..93e08c7ce1357 100755
--- a/utils/release/merge.sh
+++ b/utils/release/merge.sh
@@ -16,11 +16,13 @@ set -e
rev=""
proj=""
+revert="no"
function usage() {
echo "usage: `basename $0` [OPTIONS]"
echo " -proj PROJECT The project to merge the result into"
echo " -rev NUM The revision to merge into the project"
+ echo " -revert Revert rather than merge the commit"
}
while [ $# -gt 0 ]; do
@@ -36,6 +38,9 @@ while [ $# -gt 0 ]; do
-h | -help | --help )
usage
;;
+ -revert | --revert )
+ revert="yes"
+ ;;
* )
echo "unknown option: $1"
echo ""
@@ -60,17 +65,27 @@ fi
tempfile=`mktemp /tmp/merge.XXXXXX` || exit 1
-echo "Merging r$rev:" > $tempfile
+if [ $revert = "yes" ]; then
+ echo "Reverting r$rev:" > $tempfile
+else
+ echo "Merging r$rev:" > $tempfile
+fi
svn log -c $rev http://llvm.org/svn/llvm-project/$proj/trunk >> $tempfile 2>&1
cd $proj.src
echo "# Updating tree"
svn up
-echo "# Merging r$rev into $proj locally"
-svn merge -c $rev https://llvm.org/svn/llvm-project/$proj/trunk . || exit 1
+
+if [ $revert = "yes" ]; then
+ echo "# Reverting r$rev in $proj locally"
+ svn merge -c -$rev . || exit 1
+else
+ echo "# Merging r$rev into $proj locally"
+ svn merge -c $rev https://llvm.org/svn/llvm-project/$proj/trunk . || exit 1
+fi
echo
-echo "# To commit the merge, run the following in $proj.src/:"
+echo "# To commit, run the following in $proj.src/:"
echo svn commit -F $tempfile
exit 0
diff --git a/utils/release/test-release.sh b/utils/release/test-release.sh
index 568c097badd3a..bb1f7869467ac 100755
--- a/utils/release/test-release.sh
+++ b/utils/release/test-release.sh
@@ -34,10 +34,11 @@ do_rt="yes"
do_libs="yes"
do_libunwind="yes"
do_test_suite="yes"
-do_openmp="no"
+do_openmp="yes"
BuildDir="`pwd`"
use_autoconf="no"
ExtraConfigureFlags=""
+ExportBranch=""
function usage() {
echo "usage: `basename $0` -release X.Y.Z -rc NUM [OPTIONS]"
@@ -55,11 +56,13 @@ function usage() {
echo " -use-gzip Use gzip instead of xz."
echo " -configure-flags FLAGS Extra flags to pass to the configure step."
echo " -use-autoconf Use autoconf instead of cmake"
+ echo " -svn-path DIR Use the specified DIR instead of a release."
+ echo " For example -svn-path trunk or -svn-path branches/release_37"
echo " -no-rt Disable check-out & build Compiler-RT"
echo " -no-libs Disable check-out & build libcxx/libcxxabi/libunwind"
echo " -no-libunwind Disable check-out & build libunwind"
echo " -no-test-suite Disable check-out & build test-suite"
- echo " -openmp Check out and build the OpenMP run-time (experimental)"
+ echo " -no-openmp Disable check-out & build libomp"
}
if [ `uname -s` = "Darwin" ]; then
@@ -81,6 +84,16 @@ while [ $# -gt 0 ]; do
-final | --final )
RC=final
;;
+ -svn-path | --svn-path )
+ shift
+ Release="test"
+ Release_no_dot="test"
+ ExportBranch="$1"
+ RC="`echo $ExportBranch | sed -e 's,/,_,g'`"
+ echo "WARNING: Using the branch $ExportBranch instead of a release tag"
+ echo " This is intended to aid new packagers in trialing "
+ echo " builds without requiring a tag to be created first"
+ ;;
-triple | --triple )
shift
Triple="$1"
@@ -130,8 +143,8 @@ while [ $# -gt 0 ]; do
-no-test-suite )
do_test_suite="no"
;;
- -openmp )
- do_openmp="yes"
+ -no-openmp )
+ do_openmp="no"
;;
-help | --help | -h | --h | -\? )
usage
@@ -155,6 +168,9 @@ if [ -z "$RC" ]; then
echo "error: no release candidate number specified"
exit 1
fi
+if [ -z "$ExportBranch" ]; then
+ ExportBranch="tags/RELEASE_$Release_no_dot/$RC"
+fi
if [ -z "$Triple" ]; then
echo "error: no target triple specified"
exit 1
@@ -238,8 +254,8 @@ function check_valid_urls() {
for proj in $projects ; do
echo "# Validating $proj SVN URL"
- if ! svn ls $Base_url/$proj/tags/RELEASE_$Release_no_dot/$RC > /dev/null 2>&1 ; then
- echo "$proj $Release release candidate $RC doesn't exist!"
+ if ! svn ls $Base_url/$proj/$ExportBranch > /dev/null 2>&1 ; then
+ echo "$proj does not have a $ExportBranch branch/tag!"
exit 1
fi
done
@@ -255,7 +271,7 @@ function export_sources() {
continue
fi
echo "# Exporting $proj $Release-$RC sources"
- if ! svn export -q $Base_url/$proj/tags/RELEASE_$Release_no_dot/$RC $proj.src ; then
+ if ! svn export -q $Base_url/$proj/$ExportBranch $proj.src ; then
echo "error: failed to export $proj project"
exit 1
fi
@@ -277,6 +293,9 @@ function export_sources() {
if [ -d $BuildDir/compiler-rt.src ] && [ ! -h compiler-rt ]; then
ln -s ../../compiler-rt.src compiler-rt
fi
+ if [ -d $BuildDir/openmp.src ] && [ ! -h openmp ]; then
+ ln -s ../../openmp.src openmp
+ fi
if [ -d $BuildDir/libcxx.src ] && [ ! -h libcxx ]; then
ln -s ../../libcxx.src libcxx
fi
@@ -427,46 +446,6 @@ function package_release() {
cd $cwd
}
-# Build and package the OpenMP run-time. This is still experimental and not
-# meant for official testing in the release, but as a way for providing
-# binaries as a convenience to those who want to try it out.
-function build_OpenMP() {
- cwd=`pwd`
-
- rm -rf $BuildDir/Phase3/openmp
- rm -rf $BuildDir/Phase3/openmp.install
- mkdir -p $BuildDir/Phase3/openmp
- cd $BuildDir/Phase3/openmp
- clang=$BuildDir/Phase3/Release/llvmCore-$Release-$RC.install/usr/local/bin/clang
-
- echo "#" cmake -DCMAKE_C_COMPILER=${clang} -DCMAKE_CXX_COMPILER=${clang}++ \
- -DCMAKE_BUILD_TYPE=Release -DLIBOMP_MICRO_TESTS=on \
- $BuildDir/openmp.src/runtime
- cmake -DCMAKE_C_COMPILER=${clang} -DCMAKE_CXX_COMPILER=${clang}++ \
- -DCMAKE_BUILD_TYPE=Release -DLIBOMP_MICRO_TESTS=on \
- $BuildDir/openmp.src/runtime
-
- echo "# Building OpenMP run-time"
- echo "# ${MAKE} -j $NumJobs VERBOSE=1"
- ${MAKE} -j $NumJobs VERBOSE=1
- echo "# ${MAKE} libomp-micro-tests VERBOSE=1"
- ${MAKE} libomp-micro-tests VERBOSE=1
- echo "# ${MAKE} install DESTDIR=$BuildDir/Phase3/openmp.install"
- ${MAKE} install DESTDIR=$BuildDir/Phase3/openmp.install
-
- OpenMPPackage=OpenMP-$Release
- if [ $RC != "final" ]; then
- OpenMPPackage=$OpenMPPackage-$RC
- fi
- OpenMPPackage=$OpenMPPackage-$Triple
-
- mv $BuildDir/Phase3/openmp.install/usr/local $BuildDir/$OpenMPPackage
- cd $BuildDir
- tar cvfJ $BuildDir/$OpenMPPackage.tar.xz $OpenMPPackage
- mv $OpenMPPackage $BuildDir/Phase3/openmp.install/usr/local
- cd $cwd
-}
-
# Exit if any command fails
# Note: pipefail is necessary for running build commands through
# a pipe (i.e. it changes the output of ``false | tee /dev/null ; echo $?``)
@@ -578,10 +557,6 @@ for Flavor in $Flavors ; do
fi
done
-if [ $do_openmp = "yes" ]; then
- build_OpenMP
-fi
-
) 2>&1 | tee $LogDir/testing.$Release-$RC.log
package_release
diff --git a/utils/schedcover.py b/utils/schedcover.py
new file mode 100644
index 0000000000000..ad9b78b207f16
--- /dev/null
+++ b/utils/schedcover.py
@@ -0,0 +1,77 @@
+#!/usr/bin/python
+
+# This creates a CSV file from the output of the debug output of subtarget:
+# llvm-tblgen --gen-subtarget --debug-only=subtarget-emitter
+# With thanks to Dave Estes for mentioning the idea at 2014 LLVM Developers' Meeting
+
+import os;
+import sys;
+import re;
+import operator;
+
+table = {}
+models = set()
+filt = None
+
+def add(instr, model, resource=None):
+ global table, models
+
+ entry = table.setdefault(instr, dict())
+ entry[model] = resource
+ models.add(model)
+
+def filter_model(m):
+ global filt
+ if m and filt:
+ return filt.search(m) != None
+ else:
+ return True
+
+
+def display():
+ global table, models
+
+ ordered_table = sorted(table.items(), key=operator.itemgetter(0))
+ ordered_models = filter(filter_model, sorted(models))
+
+ # print header
+ sys.stdout.write("instruction")
+ for model in ordered_models:
+ if not model: model = "default"
+ sys.stdout.write(", {}".format(model))
+ sys.stdout.write(os.linesep)
+
+ for (instr, mapping) in ordered_table:
+ sys.stdout.write(instr)
+ for model in ordered_models:
+ if model in mapping:
+ sys.stdout.write(", {}".format(mapping[model]))
+ else:
+ sys.stdout.write(", ")
+ sys.stdout.write(os.linesep)
+
+
+def machineModelCover(path):
+ # The interesting bits
+ re_sched_default = re.compile("SchedRW machine model for ([^ ]*) (.*)\n");
+ re_sched_no_default = re.compile("No machine model for ([^ ]*)\n");
+ re_sched_spec = re.compile("InstRW on ([^ ]*) for ([^ ]*) (.*)\n");
+ re_sched_no_spec = re.compile("No machine model for ([^ ]*) on processor (.*)\n");
+
+ # scan the file
+ with open(path, 'r') as f:
+ for line in f.readlines():
+ match = re_sched_default.match(line)
+ if match: add(match.group(1), None, match.group(2))
+ match = re_sched_no_default.match(line)
+ if match: add(match.group(1), None)
+ match = re_sched_spec.match(line)
+ if match: add(match.group(2), match.group(1), match.group(3))
+ match = re_sched_no_default.match(line)
+ if match: add(match.group(1), None)
+
+ display()
+
+if len(sys.argv) > 2:
+ filt = re.compile(sys.argv[2], re.IGNORECASE)
+machineModelCover(sys.argv[1])
diff --git a/utils/shuffle_fuzz.py b/utils/shuffle_fuzz.py
index 985d1dab9e23e..eac34422b3fb5 100755
--- a/utils/shuffle_fuzz.py
+++ b/utils/shuffle_fuzz.py
@@ -198,7 +198,7 @@ define i32 @main() {
entry:
; Create a scratch space to print error messages.
%%str = alloca [128 x i8]
- %%str.ptr = getelementptr inbounds [128 x i8]* %%str, i32 0, i32 0
+ %%str.ptr = getelementptr inbounds [128 x i8], [128 x i8]* %%str, i32 0, i32 0
; Build the input vector and call the test function.
%%v = call fastcc <%(N)d x %(T)s> @test_wrapper(%(inputs)s)
@@ -233,7 +233,7 @@ die.%(i)d:
; Capture the actual value and print an error message.
%%tmp.%(i)d = zext %(IT)s %%v.%(i)d to i2048
%%bad.%(i)d = trunc i2048 %%tmp.%(i)d to i32
- call i32 (i8*, i8*, ...)* @sprintf(i8* %%str.ptr, i8* getelementptr inbounds ([128 x i8]* @error.%(i)d, i32 0, i32 0), i32 %%bad.%(i)d)
+ call i32 (i8*, i8*, ...) @sprintf(i8* %%str.ptr, i8* getelementptr inbounds ([128 x i8], [128 x i8]* @error.%(i)d, i32 0, i32 0), i32 %%bad.%(i)d)
%%length.%(i)d = call i32 @strlen(i8* %%str.ptr)
call i32 @write(i32 2, i8* %%str.ptr, i32 %%length.%(i)d)
call void @llvm.trap()
diff --git a/utils/unittest/googletest/src/gtest-internal-inl.h b/utils/unittest/googletest/src/gtest-internal-inl.h
index 35e865ff07fe9..f610cfd17b854 100644
--- a/utils/unittest/googletest/src/gtest-internal-inl.h
+++ b/utils/unittest/googletest/src/gtest-internal-inl.h
@@ -151,63 +151,67 @@ class GTestFlagSaver {
public:
// The c'tor.
GTestFlagSaver() {
- also_run_disabled_tests_ = GTEST_FLAG(also_run_disabled_tests);
- break_on_failure_ = GTEST_FLAG(break_on_failure);
- catch_exceptions_ = GTEST_FLAG(catch_exceptions);
color_ = GTEST_FLAG(color);
death_test_style_ = GTEST_FLAG(death_test_style);
- death_test_use_fork_ = GTEST_FLAG(death_test_use_fork);
filter_ = GTEST_FLAG(filter);
internal_run_death_test_ = GTEST_FLAG(internal_run_death_test);
- list_tests_ = GTEST_FLAG(list_tests);
output_ = GTEST_FLAG(output);
- print_time_ = GTEST_FLAG(print_time);
+ stream_result_to_ = GTEST_FLAG(stream_result_to);
+
random_seed_ = GTEST_FLAG(random_seed);
repeat_ = GTEST_FLAG(repeat);
- shuffle_ = GTEST_FLAG(shuffle);
stack_trace_depth_ = GTEST_FLAG(stack_trace_depth);
- stream_result_to_ = GTEST_FLAG(stream_result_to);
+
+ also_run_disabled_tests_ = GTEST_FLAG(also_run_disabled_tests);
+ break_on_failure_ = GTEST_FLAG(break_on_failure);
+ catch_exceptions_ = GTEST_FLAG(catch_exceptions);
+ death_test_use_fork_ = GTEST_FLAG(death_test_use_fork);
+ list_tests_ = GTEST_FLAG(list_tests);
+ print_time_ = GTEST_FLAG(print_time);
+ shuffle_ = GTEST_FLAG(shuffle);
throw_on_failure_ = GTEST_FLAG(throw_on_failure);
}
// The d'tor is not virtual. DO NOT INHERIT FROM THIS CLASS.
~GTestFlagSaver() {
- GTEST_FLAG(also_run_disabled_tests) = also_run_disabled_tests_;
- GTEST_FLAG(break_on_failure) = break_on_failure_;
- GTEST_FLAG(catch_exceptions) = catch_exceptions_;
GTEST_FLAG(color) = color_;
GTEST_FLAG(death_test_style) = death_test_style_;
- GTEST_FLAG(death_test_use_fork) = death_test_use_fork_;
GTEST_FLAG(filter) = filter_;
GTEST_FLAG(internal_run_death_test) = internal_run_death_test_;
- GTEST_FLAG(list_tests) = list_tests_;
GTEST_FLAG(output) = output_;
- GTEST_FLAG(print_time) = print_time_;
+ GTEST_FLAG(stream_result_to) = stream_result_to_;
+
GTEST_FLAG(random_seed) = random_seed_;
GTEST_FLAG(repeat) = repeat_;
- GTEST_FLAG(shuffle) = shuffle_;
GTEST_FLAG(stack_trace_depth) = stack_trace_depth_;
- GTEST_FLAG(stream_result_to) = stream_result_to_;
+
+ GTEST_FLAG(also_run_disabled_tests) = also_run_disabled_tests_;
+ GTEST_FLAG(break_on_failure) = break_on_failure_;
+ GTEST_FLAG(catch_exceptions) = catch_exceptions_;
+ GTEST_FLAG(death_test_use_fork) = death_test_use_fork_;
+ GTEST_FLAG(list_tests) = list_tests_;
+ GTEST_FLAG(print_time) = print_time_;
+ GTEST_FLAG(shuffle) = shuffle_;
GTEST_FLAG(throw_on_failure) = throw_on_failure_;
}
private:
// Fields for saving the original values of flags.
- bool also_run_disabled_tests_;
- bool break_on_failure_;
- bool catch_exceptions_;
String color_;
String death_test_style_;
- bool death_test_use_fork_;
String filter_;
String internal_run_death_test_;
- bool list_tests_;
String output_;
- bool print_time_;
+ String stream_result_to_;
internal::Int32 random_seed_;
internal::Int32 repeat_;
- bool shuffle_;
internal::Int32 stack_trace_depth_;
- String stream_result_to_;
+ bool also_run_disabled_tests_;
+ bool break_on_failure_;
+ bool catch_exceptions_;
+ bool death_test_use_fork_;
+ bool list_tests_;
+ bool print_time_;
+ bool shuffle_;
bool throw_on_failure_;
} GTEST_ATTRIBUTE_UNUSED_;
diff --git a/utils/update_llc_test_checks.py b/utils/update_llc_test_checks.py
index df01d8973c464..cfdf830907f50 100755
--- a/utils/update_llc_test_checks.py
+++ b/utils/update_llc_test_checks.py
@@ -73,6 +73,8 @@ def main():
flags=(re.M | re.S))
check_prefix_re = re.compile('--check-prefix=(\S+)')
check_re = re.compile(r'^\s*;\s*([^:]+?)(?:-NEXT|-NOT|-DAG|-LABEL)?:')
+ autogenerated_note = ('; NOTE: Assertions have been autogenerated by '
+ 'utils/update_llc_test_checks.py')
for test in args.tests:
if args.verbose:
@@ -136,7 +138,7 @@ def main():
if f in asm[prefix] and asm[prefix][f] != f_asm:
if prefix == prefixes[-1]:
print >>sys.stderr, ('WARNING: Found conflicting asm under the '
- 'same prefix!')
+ 'same prefix: %r!' % (prefix,))
else:
asm[prefix][f] = None
continue
@@ -149,6 +151,8 @@ def main():
if args.verbose:
print >>sys.stderr, 'Rewriting FileCheck prefixes: %s' % (prefix_set,)
fixed_lines = []
+ fixed_lines.append(autogenerated_note)
+
for l in test_lines:
if is_in_function_start:
if l.lstrip().startswith(';'):
@@ -191,6 +195,8 @@ def main():
is_in_function = False
continue
+ if l == autogenerated_note:
+ continue
fixed_lines.append(l)
m = ir_function_re.match(l)
diff --git a/utils/vim/ftplugin/llvm.vim b/utils/vim/ftplugin/llvm.vim
index 04bb9e0016966..bdf49c92ff22b 100644
--- a/utils/vim/ftplugin/llvm.vim
+++ b/utils/vim/ftplugin/llvm.vim
@@ -9,3 +9,4 @@ let b:did_ftplugin = 1
setlocal softtabstop=2 shiftwidth=2
setlocal expandtab
+setlocal comments+=:;
diff --git a/utils/vim/syntax/llvm.vim b/utils/vim/syntax/llvm.vim
index c8e73cd94a57d..68a53ec2383d9 100644
--- a/utils/vim/syntax/llvm.vim
+++ b/utils/vim/syntax/llvm.vim
@@ -1,7 +1,7 @@
" Vim syntax file
" Language: llvm
" Maintainer: The LLVM team, http://llvm.org/
-" Version: $Revision: 235369 $
+" Version: $Revision: 256512 $
if version < 600
syntax clear
@@ -16,7 +16,7 @@ syn case match
" benefit as much from having dedicated highlighting rules.
syn keyword llvmType void half float double x86_fp80 fp128 ppc_fp128
syn keyword llvmType label metadata x86_mmx
-syn keyword llvmType type label opaque
+syn keyword llvmType type label opaque token
syn match llvmType /\<i\d\+\>/
" Instructions.
@@ -42,10 +42,11 @@ syn keyword llvmKeyword arm_aapcscc arm_apcscc asm atomic available_externally
syn keyword llvmKeyword blockaddress byval c catch cc ccc cleanup coldcc common
syn keyword llvmKeyword constant datalayout declare default define deplibs
syn keyword llvmKeyword distinct dllexport dllimport except extern_weak external
-syn keyword llvmKeyword externally_initialized fastcc filter gc global hidden
-syn keyword llvmKeyword initialexec inlinehint inreg intel_ocl_bicc inteldialect
-syn keyword llvmKeyword internal linkonce linkonce_odr localdynamic localexec
-syn keyword llvmKeyword minsize module monotonic msp430_intrcc naked nest
+syn keyword llvmKeyword externally_initialized fastcc filter gc global hhvmcc
+syn keyword llvmKeyword hhvm_ccc hidden initialexec inlinehint inreg
+syn keyword llvmKeyword intel_ocl_bicc inteldialect internal linkonce
+syn keyword llvmKeyword linkonce_odr localdynamic localexec minsize module
+syn keyword llvmKeyword monotonic msp430_intrcc musttail naked nest
syn keyword llvmKeyword noalias nocapture noimplicitfloat noinline nonlazybind
syn keyword llvmKeyword noredzone noreturn nounwind optnone optsize personality
syn keyword llvmKeyword private protected ptx_device ptx_kernel readnone
@@ -56,7 +57,7 @@ syn keyword llvmKeyword sspstrong tail target thread_local to triple
syn keyword llvmKeyword unnamed_addr unordered uwtable volatile weak weak_odr
syn keyword llvmKeyword x86_fastcallcc x86_stdcallcc x86_thiscallcc
syn keyword llvmKeyword x86_64_sysvcc x86_64_win64cc zeroext uselistorder
-syn keyword llvmKeyword uselistorder_bb
+syn keyword llvmKeyword uselistorder_bb musttail
" Obsolete keywords.
syn keyword llvmError getresult begin end
@@ -84,10 +85,10 @@ syn match llvmConstant /\<DW_LANG_[a-zA-Z0-9_]\+\>/
syn match llvmConstant /\<DW_VIRTUALITY_[a-z_]\+\>/
syn match llvmConstant /\<DIFlag[A-Za-z]\+\>/
-" Syntax-highlight dejagnu test commands.
-syn match llvmSpecialComment /;\s*RUN:.*$/
+" Syntax-highlight lit test commands and bug numbers.
syn match llvmSpecialComment /;\s*PR\d*\s*$/
-syn match llvmSpecialComment /;\s*END\.\s*$/
+syn match llvmSpecialComment /;\s*REQUIRES:.*$/
+syn match llvmSpecialComment /;\s*RUN:.*$/
syn match llvmSpecialComment /;\s*XFAIL:.*$/
if version >= 508 || !exists("did_c_syn_inits")