1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-22 10:42:39 +01:00

[TableGen] Fix sort order of asm operand classes

This is a fix for https://llvm.org/bugs/show_bug.cgi?id=22796.

The previous implementation of ClassInfo::operator< allowed cycles of classes
such that x < y < z < x, meaning that a list of them cannot be correctly
sorted, and the sort order could differ with different standard libraries.

The original implementation sorted classes by ValueName if they were otherwise
equal. This isn't strictly necessary, but some backends seem to accidentally
rely on it. If I reverse this comparison I get 8 test failures spread across
the AArch64, Mips and X86 backends, so I have left it in until those backends
can be fixed.

There was one case in the X86 backend where the observable behaviour of the
assembler is changed by this patch. This was because some of the memory asm
operands were not marked as children of X86MemAsmOperand.

Differential Revision: http://reviews.llvm.org/D16141

llvm-svn: 258677
This commit is contained in:
Oliver Stannard 2016-01-25 10:20:19 +00:00
parent 2adffa1342
commit 4ff6a9f924
2 changed files with 87 additions and 22 deletions

View File

@ -263,7 +263,7 @@ def ptr_rc_nosp : PointerLikeRegClass<1>;
def X86MemAsmOperand : AsmOperandClass {
let Name = "Mem";
}
let RenderMethod = "addMemOperands" in {
let RenderMethod = "addMemOperands", SuperClasses = [X86MemAsmOperand] in {
def X86Mem8AsmOperand : AsmOperandClass { let Name = "Mem8"; }
def X86Mem16AsmOperand : AsmOperandClass { let Name = "Mem16"; }
def X86Mem32AsmOperand : AsmOperandClass { let Name = "Mem32"; }

View File

@ -263,34 +263,79 @@ public:
return false;
}
/// operator< - Compare two classes.
// FIXME: This ordering seems to be broken. For example:
// u64 < i64, i64 < s8, s8 < u64, forming a cycle
// u64 is a subset of i64
// i64 and s8 are not subsets of each other, so are ordered by name
// s8 and u64 are not subsets of each other, so are ordered by name
int getTreeDepth() const {
int Depth = 0;
const ClassInfo *Root = this;
while (!Root->SuperClasses.empty()) {
Depth++;
Root = Root->SuperClasses.front();
}
return Depth;
}
const ClassInfo *findRoot() const {
const ClassInfo *Root = this;
while (!Root->SuperClasses.empty())
Root = Root->SuperClasses.front();
return Root;
}
/// Compare two classes. This does not produce a total ordering, but does
/// guarantee that subclasses are sorted before their parents, and that the
/// ordering is transitive.
bool operator<(const ClassInfo &RHS) const {
if (this == &RHS)
return false;
// Unrelated classes can be ordered by kind.
if (!isRelatedTo(RHS))
return Kind < RHS.Kind;
switch (Kind) {
case Invalid:
llvm_unreachable("Invalid kind!");
default:
// This class precedes the RHS if it is a proper subset of the RHS.
if (isSubsetOf(RHS))
// First, enforce the ordering between the three different types of class.
// Tokens sort before registers, which sort before user classes.
if (Kind == Token) {
if (RHS.Kind != Token)
return true;
if (RHS.isSubsetOf(*this))
assert(RHS.Kind == Token);
} else if (isRegisterClass()) {
if (RHS.Kind == Token)
return false;
// Otherwise, order by name to ensure we have a total ordering.
return ValueName < RHS.ValueName;
else if (RHS.isUserClass())
return true;
assert(RHS.isRegisterClass());
} else if (isUserClass()) {
if (!RHS.isUserClass())
return false;
assert(RHS.isUserClass());
} else {
llvm_unreachable("Unknown ClassInfoKind");
}
if (Kind == Token || isUserClass()) {
// Related tokens and user classes get sorted by depth in the inheritence
// tree (so that subclasses are before their parents).
if (isRelatedTo(RHS)) {
if (getTreeDepth() > RHS.getTreeDepth())
return true;
if (getTreeDepth() < RHS.getTreeDepth())
return false;
} else {
// Unrelated tokens and user classes are ordered by the name of their
// root nodes, so that there is a consistent ordering between
// unconnected trees.
return findRoot()->ValueName < RHS.findRoot()->ValueName;
}
} else if (isRegisterClass()) {
// For register sets, sort by number of registers. This guarantees that
// a set will always sort before all of it's strict supersets.
if (Registers.size() != RHS.Registers.size())
return Registers.size() < RHS.Registers.size();
} else {
llvm_unreachable("Unknown ClassInfoKind");
}
// FIXME: We should be able to just return false here, as we only need a
// partial order (we use stable sorts, so this is deterministic) and the
// name of a class shouldn't be significant. However, some of the backends
// accidentally rely on this behaviour, so it will have to stay like this
// until they are fixed.
return ValueName < RHS.ValueName;
}
};
@ -1539,6 +1584,16 @@ void AsmMatcherInfo::buildInfo() {
// Reorder classes so that classes precede super classes.
Classes.sort();
#ifndef NDEBUG
// Verify that the table is now sorted
for (auto I = Classes.begin(), E = Classes.end(); I != E; ++I) {
for (auto J = I; J != E; ++J) {
assert(!(*J < *I));
assert(I == J || !J->isSubsetOf(*I));
}
}
#endif
}
/// buildInstructionOperandReference - The specified operand is a reference to a
@ -2665,6 +2720,16 @@ void AsmMatcherEmitter::run(raw_ostream &OS) {
const std::unique_ptr<MatchableInfo> &b){
return *a < *b;});
#ifndef NDEBUG
// Verify that the table is now sorted
for (auto I = Info.Matchables.begin(), E = Info.Matchables.end(); I != E;
++I) {
for (auto J = I; J != E; ++J) {
assert(!(**J < **I));
}
}
#endif
DEBUG_WITH_TYPE("instruction_info", {
for (const auto &MI : Info.Matchables)
MI->dump();