IR: Implement uselistorder assembly directives

Implement `uselistorder` and `uselistorder_bb` assembly directives,
which allow the use-list order to be recovered when round-tripping to
assembly.

This is the bulk of PR20515.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@216025 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Duncan P. N. Exon Smith 2014-08-19 21:30:15 +00:00
parent 722885f5f1
commit 7838818ad7
31 changed files with 709 additions and 7 deletions

View File

@ -1749,6 +1749,45 @@ otherwise unsafe floating point operations
dramatically change results in floating point (e.g. reassociate). This
flag implies all the others.
.. _uselistorder:
Use-list Order Directives
-------------------------
Use-list directives encode the in-memory order of each use-list, allowing the
order to be recreated. ``<order-indexes>`` is a comma-separated list of
indexes that are assigned to the referenced value's uses. The referenced
value's use-list is immediately sorted by these indexes.
Use-list directives may appear at function scope or global scope. They are not
instructions, and have no effect on the semantics of the IR. When they're at
function scope, they must appear after the terminator of the final basic block.
If basic blocks have their address taken via ``blockaddress()`` expressions,
``uselistorder_bb`` can be used to reorder their use-lists from outside their
function's scope.
:Syntax:
::
uselistorder <ty> <value>, { <order-indexes> }
uselistorder_bb @function, %block { <order-indexes> }
:Examples:
::
; At function scope.
uselistorder i32 %arg1, { 1, 0, 2 }
uselistorder label %bb, { 1, 0 }
; At global scope.
uselistorder i32* @global, { 1, 2, 0 }
uselistorder i32 7, { 1, 0 }
uselistorder i32 (i32) @bar, { 1, 0 }
uselistorder_bb @foo, %bb, { 5, 1, 3, 2, 0, 4 }
.. _typesystem:
Type System

View File

@ -664,6 +664,10 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(x);
KEYWORD(blockaddress);
// Use-list order directives.
KEYWORD(uselistorder);
KEYWORD(uselistorder_bb);
KEYWORD(personality);
KEYWORD(cleanup);
KEYWORD(catch);

View File

@ -239,6 +239,9 @@ bool LLParser::ParseTopLevelEntities() {
}
case lltok::kw_attributes: if (ParseUnnamedAttrGrp()) return true; break;
case lltok::kw_uselistorder: if (ParseUseListOrder()) return true; break;
case lltok::kw_uselistorder_bb:
if (ParseUseListOrderBB()) return true; break;
}
}
}
@ -3371,8 +3374,7 @@ bool LLParser::PerFunctionState::resolveForwardRefBlockAddresses() {
}
/// ParseFunctionBody
/// ::= '{' BasicBlock+ '}'
///
/// ::= '{' BasicBlock+ UseListOrderDirective* '}'
bool LLParser::ParseFunctionBody(Function &Fn) {
if (Lex.getKind() != lltok::lbrace)
return TokError("expected '{' in function body");
@ -3390,12 +3392,17 @@ bool LLParser::ParseFunctionBody(Function &Fn) {
SaveAndRestore<PerFunctionState *> ScopeExit(BlockAddressPFS, &PFS);
// We need at least one basic block.
if (Lex.getKind() == lltok::rbrace)
if (Lex.getKind() == lltok::rbrace || Lex.getKind() == lltok::kw_uselistorder)
return TokError("function body requires at least one basic block");
while (Lex.getKind() != lltok::rbrace)
while (Lex.getKind() != lltok::rbrace &&
Lex.getKind() != lltok::kw_uselistorder)
if (ParseBasicBlock(PFS)) return true;
while (Lex.getKind() != lltok::rbrace)
if (ParseUseListOrder(&PFS))
return true;
// Eat the }.
Lex.Lex();
@ -4654,3 +4661,135 @@ bool LLParser::ParseMDNodeVector(SmallVectorImpl<Value*> &Elts,
return false;
}
//===----------------------------------------------------------------------===//
// Use-list order directives.
//===----------------------------------------------------------------------===//
bool LLParser::sortUseListOrder(Value *V, ArrayRef<unsigned> Indexes,
SMLoc Loc) {
if (V->use_empty())
return Error(Loc, "value has no uses");
unsigned NumUses = 0;
SmallDenseMap<const Use *, unsigned, 16> Order;
for (const Use &U : V->uses()) {
if (++NumUses > Indexes.size())
break;
Order[&U] = Indexes[NumUses - 1];
}
if (NumUses < 2)
return Error(Loc, "value only has one use");
if (Order.size() != Indexes.size() || NumUses > Indexes.size())
return Error(Loc, "wrong number of indexes, expected " +
Twine(std::distance(V->use_begin(), V->use_end())));
V->sortUseList([&](const Use &L, const Use &R) {
return Order.lookup(&L) < Order.lookup(&R);
});
return false;
}
/// ParseUseListOrderIndexes
/// ::= '{' uint32 (',' uint32)+ '}'
bool LLParser::ParseUseListOrderIndexes(SmallVectorImpl<unsigned> &Indexes) {
SMLoc Loc = Lex.getLoc();
if (ParseToken(lltok::lbrace, "expected '{' here"))
return true;
if (Lex.getKind() == lltok::rbrace)
return Lex.Error("expected non-empty list of uselistorder indexes");
// Use Offset, Max, and IsOrdered to check consistency of indexes. The
// indexes should be distinct numbers in the range [0, size-1], and should
// not be in order.
unsigned Offset = 0;
unsigned Max = 0;
bool IsOrdered = true;
assert(Indexes.empty() && "Expected empty order vector");
do {
unsigned Index;
if (ParseUInt32(Index))
return true;
// Update consistency checks.
Offset += Index - Indexes.size();
Max = std::max(Max, Index);
IsOrdered &= Index == Indexes.size();
Indexes.push_back(Index);
} while (EatIfPresent(lltok::comma));
if (ParseToken(lltok::rbrace, "expected '}' here"))
return true;
if (Indexes.size() < 2)
return Error(Loc, "expected >= 2 uselistorder indexes");
if (Offset != 0 || Max >= Indexes.size())
return Error(Loc, "expected distinct uselistorder indexes in range [0, size)");
if (IsOrdered)
return Error(Loc, "expected uselistorder indexes to change the order");
return false;
}
/// ParseUseListOrder
/// ::= 'uselistorder' Type Value ',' UseListOrderIndexes
bool LLParser::ParseUseListOrder(PerFunctionState *PFS) {
SMLoc Loc = Lex.getLoc();
if (ParseToken(lltok::kw_uselistorder, "expected uselistorder directive"))
return true;
Value *V;
SmallVector<unsigned, 16> Indexes;
if (ParseTypeAndValue(V, PFS) ||
ParseToken(lltok::comma, "expected comma in uselistorder directive") ||
ParseUseListOrderIndexes(Indexes))
return true;
return sortUseListOrder(V, Indexes, Loc);
}
/// ParseUseListOrderBB
/// ::= 'uselistorder_bb' @foo ',' %bar ',' UseListOrderIndexes
bool LLParser::ParseUseListOrderBB() {
assert(Lex.getKind() == lltok::kw_uselistorder_bb);
SMLoc Loc = Lex.getLoc();
Lex.Lex();
ValID Fn, Label;
SmallVector<unsigned, 16> Indexes;
if (ParseValID(Fn) ||
ParseToken(lltok::comma, "expected comma in uselistorder_bb directive") ||
ParseValID(Label) ||
ParseToken(lltok::comma, "expected comma in uselistorder_bb directive") ||
ParseUseListOrderIndexes(Indexes))
return true;
// Check the function.
GlobalValue *GV;
if (Fn.Kind == ValID::t_GlobalName)
GV = M->getNamedValue(Fn.StrVal);
else if (Fn.Kind == ValID::t_GlobalID)
GV = Fn.UIntVal < NumberedVals.size() ? NumberedVals[Fn.UIntVal] : nullptr;
else
return Error(Fn.Loc, "expected function name in uselistorder_bb");
if (!GV)
return Error(Fn.Loc, "invalid function forward reference in uselistorder_bb");
auto *F = dyn_cast<Function>(GV);
if (!F)
return Error(Fn.Loc, "expected function name in uselistorder_bb");
if (F->isDeclaration())
return Error(Fn.Loc, "invalid declaration in uselistorder_bb");
// Check the basic block.
if (Label.Kind == ValID::t_LocalID)
return Error(Label.Loc, "invalid numeric label in uselistorder_bb");
if (Label.Kind != ValID::t_LocalName)
return Error(Label.Loc, "expected basic block name in uselistorder_bb");
Value *V = F->getValueSymbolTable().lookup(Label.StrVal);
if (!V)
return Error(Label.Loc, "invalid basic block in uselistorder_bb");
if (!isa<BasicBlock>(V))
return Error(Label.Loc, "expected basic block in uselistorder_bb");
return sortUseListOrder(V, Indexes, Loc);
}

View File

@ -438,6 +438,12 @@ namespace llvm {
int ParseGetElementPtr(Instruction *&I, PerFunctionState &PFS);
int ParseExtractValue(Instruction *&I, PerFunctionState &PFS);
int ParseInsertValue(Instruction *&I, PerFunctionState &PFS);
// Use-list order directives.
bool ParseUseListOrder(PerFunctionState *PFS = nullptr);
bool ParseUseListOrderBB();
bool ParseUseListOrderIndexes(SmallVectorImpl<unsigned> &Indexes);
bool sortUseListOrder(Value *V, ArrayRef<unsigned> Indexes, SMLoc Loc);
};
} // End llvm namespace

View File

@ -180,6 +180,9 @@ namespace lltok {
kw_extractelement, kw_insertelement, kw_shufflevector,
kw_extractvalue, kw_insertvalue, kw_blockaddress,
// Use-list order directives.
kw_uselistorder, kw_uselistorder_bb,
// Unsigned Valued tokens (UIntVal).
GlobalID, // @42
LocalVarID, // %42

View File

@ -49,6 +49,213 @@ AssemblyAnnotationWriter::~AssemblyAnnotationWriter() {}
// Helper Functions
//===----------------------------------------------------------------------===//
namespace {
struct OrderMap {
DenseMap<const Value *, std::pair<unsigned, bool>> IDs;
unsigned size() const { return IDs.size(); }
std::pair<unsigned, bool> &operator[](const Value *V) { return IDs[V]; }
std::pair<unsigned, bool> lookup(const Value *V) const {
return IDs.lookup(V);
}
void index(const Value *V) {
// Explicitly sequence get-size and insert-value operations to avoid UB.
unsigned ID = IDs.size() + 1;
IDs[V].first = ID;
}
};
}
static void orderValue(const Value *V, OrderMap &OM) {
if (OM.lookup(V).first)
return;
if (const Constant *C = dyn_cast<Constant>(V))
if (C->getNumOperands() && !isa<GlobalValue>(C))
for (const Value *Op : C->operands())
if (!isa<BasicBlock>(Op) && !isa<GlobalValue>(Op))
orderValue(Op, OM);
// Note: we cannot cache this lookup above, since inserting into the map
// changes the map's size, and thus affects the other IDs.
OM.index(V);
}
static OrderMap orderModule(const Module *M) {
// This needs to match the order used by ValueEnumerator::ValueEnumerator()
// and ValueEnumerator::incorporateFunction().
OrderMap OM;
for (const GlobalVariable &G : M->globals()) {
if (G.hasInitializer())
if (!isa<GlobalValue>(G.getInitializer()))
orderValue(G.getInitializer(), OM);
orderValue(&G, OM);
}
for (const GlobalAlias &A : M->aliases()) {
if (!isa<GlobalValue>(A.getAliasee()))
orderValue(A.getAliasee(), OM);
orderValue(&A, OM);
}
for (const Function &F : *M) {
if (F.hasPrefixData())
if (!isa<GlobalValue>(F.getPrefixData()))
orderValue(F.getPrefixData(), OM);
orderValue(&F, OM);
if (F.isDeclaration())
continue;
for (const Argument &A : F.args())
orderValue(&A, OM);
for (const BasicBlock &BB : F) {
orderValue(&BB, OM);
for (const Instruction &I : BB) {
for (const Value *Op : I.operands())
if ((isa<Constant>(*Op) && !isa<GlobalValue>(*Op)) ||
isa<InlineAsm>(*Op))
orderValue(Op, OM);
orderValue(&I, OM);
}
}
}
return OM;
}
static void predictValueUseListOrderImpl(const Value *V, const Function *F,
unsigned ID, const OrderMap &OM,
UseListOrderStack &Stack) {
// Predict use-list order for this one.
typedef std::pair<const Use *, unsigned> Entry;
SmallVector<Entry, 64> List;
for (const Use &U : V->uses())
// Check if this user will be serialized.
if (OM.lookup(U.getUser()).first)
List.push_back(std::make_pair(&U, List.size()));
if (List.size() < 2)
// We may have lost some users.
return;
bool GetsReversed =
!isa<GlobalVariable>(V) && !isa<Function>(V) && !isa<BasicBlock>(V);
if (auto *BA = dyn_cast<BlockAddress>(V))
ID = OM.lookup(BA->getBasicBlock()).first;
std::sort(List.begin(), List.end(), [&](const Entry &L, const Entry &R) {
const Use *LU = L.first;
const Use *RU = R.first;
if (LU == RU)
return false;
auto LID = OM.lookup(LU->getUser()).first;
auto RID = OM.lookup(RU->getUser()).first;
// If ID is 4, then expect: 7 6 5 1 2 3.
if (LID < RID) {
if (GetsReversed)
if (RID <= ID)
return true;
return false;
}
if (RID < LID) {
if (GetsReversed)
if (LID <= ID)
return false;
return true;
}
// LID and RID are equal, so we have different operands of the same user.
// Assume operands are added in order for all instructions.
if (GetsReversed)
if (LID <= ID)
return LU->getOperandNo() < RU->getOperandNo();
return LU->getOperandNo() > RU->getOperandNo();
});
if (std::is_sorted(
List.begin(), List.end(),
[](const Entry &L, const Entry &R) { return L.second < R.second; }))
// Order is already correct.
return;
// Store the shuffle.
Stack.emplace_back(V, F, List.size());
assert(List.size() == Stack.back().Shuffle.size() && "Wrong size");
for (size_t I = 0, E = List.size(); I != E; ++I)
Stack.back().Shuffle[I] = List[I].second;
}
static void predictValueUseListOrder(const Value *V, const Function *F,
OrderMap &OM, UseListOrderStack &Stack) {
auto &IDPair = OM[V];
assert(IDPair.first && "Unmapped value");
if (IDPair.second)
// Already predicted.
return;
// Do the actual prediction.
IDPair.second = true;
if (!V->use_empty() && std::next(V->use_begin()) != V->use_end())
predictValueUseListOrderImpl(V, F, IDPair.first, OM, Stack);
// Recursive descent into constants.
if (const Constant *C = dyn_cast<Constant>(V))
if (C->getNumOperands()) // Visit GlobalValues.
for (const Value *Op : C->operands())
if (isa<Constant>(Op)) // Visit GlobalValues.
predictValueUseListOrder(Op, F, OM, Stack);
}
static UseListOrderStack predictUseListOrder(const Module *M) {
OrderMap OM = orderModule(M);
// Use-list orders need to be serialized after all the users have been added
// to a value, or else the shuffles will be incomplete. Store them per
// function in a stack.
//
// Aside from function order, the order of values doesn't matter much here.
UseListOrderStack Stack;
// We want to visit the functions backward now so we can list function-local
// constants in the last Function they're used in. Module-level constants
// have already been visited above.
for (auto I = M->rbegin(), E = M->rend(); I != E; ++I) {
const Function &F = *I;
if (F.isDeclaration())
continue;
for (const BasicBlock &BB : F)
predictValueUseListOrder(&BB, &F, OM, Stack);
for (const Argument &A : F.args())
predictValueUseListOrder(&A, &F, OM, Stack);
for (const BasicBlock &BB : F)
for (const Instruction &I : BB)
for (const Value *Op : I.operands())
if (isa<Constant>(*Op) || isa<InlineAsm>(*Op)) // Visit GlobalValues.
predictValueUseListOrder(Op, &F, OM, Stack);
for (const BasicBlock &BB : F)
for (const Instruction &I : BB)
predictValueUseListOrder(&I, &F, OM, Stack);
}
// Visit globals last.
for (const GlobalVariable &G : M->globals())
predictValueUseListOrder(&G, nullptr, OM, Stack);
for (const Function &F : *M)
predictValueUseListOrder(&F, nullptr, OM, Stack);
for (const GlobalAlias &A : M->aliases())
predictValueUseListOrder(&A, nullptr, OM, Stack);
for (const GlobalVariable &G : M->globals())
if (G.hasInitializer())
predictValueUseListOrder(G.getInitializer(), nullptr, OM, Stack);
for (const GlobalAlias &A : M->aliases())
predictValueUseListOrder(A.getAliasee(), nullptr, OM, Stack);
for (const Function &F : *M)
if (F.hasPrefixData())
predictValueUseListOrder(F.getPrefixData(), nullptr, OM, Stack);
return Stack;
}
static const Module *getModuleFromVal(const Value *V) {
if (const Argument *MA = dyn_cast<Argument>(V))
return MA->getParent() ? MA->getParent()->getParent() : nullptr;
@ -347,6 +554,8 @@ public:
FunctionProcessed = false;
}
const Function *getFunction() const { return TheFunction; }
/// After calling incorporateFunction, use this method to remove the
/// most recently incorporated function from the SlotTracker. This
/// will reset the state of the machine back to just the module contents.
@ -1279,6 +1488,9 @@ void AssemblyWriter::writeParamOperand(const Value *Operand,
void AssemblyWriter::printModule(const Module *M) {
Machine.initialize();
if (shouldPreserveAssemblyUseListOrder())
UseListOrders = predictUseListOrder(M);
if (!M->getModuleIdentifier().empty() &&
// Don't print the ID if it will start a new line (which would
// require a comment char before it).
@ -1339,9 +1551,13 @@ void AssemblyWriter::printModule(const Module *M) {
I != E; ++I)
printAlias(I);
// Output global use-lists.
printUseLists(nullptr);
// Output all of the functions.
for (Module::const_iterator I = M->begin(), E = M->end(); I != E; ++I)
printFunction(I);
assert(UseListOrders.empty() && "All use-lists should have been consumed");
// Output all attribute groups.
if (!Machine.as_empty()) {
@ -1692,6 +1908,9 @@ void AssemblyWriter::printFunction(const Function *F) {
for (Function::const_iterator I = F->begin(), E = F->end(); I != E; ++I)
printBasicBlock(I);
// Output the function's use-lists.
printUseLists(F);
Out << "}\n";
}
@ -2169,6 +2388,45 @@ void AssemblyWriter::writeAllAttributeGroups() {
} // namespace llvm
void AssemblyWriter::printUseListOrder(const UseListOrder &Order) {
bool IsInFunction = Machine.getFunction();
if (IsInFunction)
Out << " ";
Out << "uselistorder";
if (const BasicBlock *BB =
IsInFunction ? nullptr : dyn_cast<BasicBlock>(Order.V)) {
Out << "_bb ";
writeOperand(BB->getParent(), false);
Out << ", ";
writeOperand(BB, false);
} else {
Out << " ";
writeOperand(Order.V, true);
}
Out << ", { ";
assert(Order.Shuffle.size() >= 2 && "Shuffle too small");
Out << Order.Shuffle[0];
for (unsigned I = 1, E = Order.Shuffle.size(); I != E; ++I)
Out << ", " << Order.Shuffle[I];
Out << " }\n";
}
void AssemblyWriter::printUseLists(const Function *F) {
auto hasMore =
[&]() { return !UseListOrders.empty() && UseListOrders.back().F == F; };
if (!hasMore())
// Nothing to do.
return;
Out << "\n; uselistorder directives\n";
while (hasMore()) {
printUseListOrder(UseListOrders.back());
UseListOrders.pop_back();
}
}
//===----------------------------------------------------------------------===//
// External Interface declarations
//===----------------------------------------------------------------------===//

View File

@ -20,6 +20,7 @@
#include "llvm/IR/Attributes.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/TypeFinder.h"
#include "llvm/IR/UseListOrder.h"
#include "llvm/Support/FormattedStream.h"
namespace llvm {
@ -73,6 +74,7 @@ private:
TypePrinting TypePrinter;
AssemblyAnnotationWriter *AnnotationWriter;
SetVector<const Comdat *> Comdats;
UseListOrderStack UseListOrders;
public:
/// Construct an AssemblyWriter with an external SlotTracker
@ -111,6 +113,9 @@ public:
void printInstructionLine(const Instruction &I);
void printInstruction(const Instruction &I);
void printUseListOrder(const UseListOrder &Order);
void printUseLists(const Function *F);
private:
void init();

View File

@ -0,0 +1,11 @@
; RUN: verify-uselistorder < %s
; Globals.
@global = global i32 0
@alias.ref1 = global i32* getelementptr inbounds (i32* @alias, i64 1)
@alias.ref2 = global i32* getelementptr inbounds (i32* @alias, i64 1)
; Aliases.
@alias = alias i32* @global
@alias.ref3 = alias i32* getelementptr inbounds (i32* @alias, i64 1)
@alias.ref4 = alias i32* getelementptr inbounds (i32* @alias, i64 1)

View File

@ -0,0 +1,37 @@
; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s
; CHECK: error: expected uselistorder directive
define i32 @f32(i32 %a, i32 %b, i32 %c, i32 %d) {
entry:
br label %first
; <label 0>:
%eh = mul i32 %e, %1
%sum = add i32 %eh, %ef
br label %preexit
preexit:
%product = phi i32 [%ef, %first], [%sum, %0]
%backto0 = icmp slt i32 %product, -9
br i1 %backto0, label %0, label %exit
first:
%e = add i32 %a, 7
%f = add i32 %b, 7
%g = add i32 %c, 8
%1 = add i32 %d, 8
%ef = mul i32 %e, %f
%g1 = mul i32 %g, %1
%goto0 = icmp slt i32 %g1, -9
br i1 %goto0, label %0, label %preexit
; uselistorder directives
uselistorder i32 7, { 1, 0 }
uselistorder i32 %1, { 1, 0 }
uselistorder i32 %e, { 1, 0 }
uselistorder label %0, { 1, 0 }
uselistorder label %preexit, { 1, 0 }
exit:
ret i32 %product
}

View File

@ -0,0 +1,6 @@
; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s
; CHECK: error: value has no uses
define void @foo() {
unreachable
uselistorder i32 %val, { 1, 0 }
}

View File

@ -0,0 +1,6 @@
; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s
; CHECK: error: value has no uses
define void @foo() {
unreachable
uselistorder i32 %1, { 1, 0 }
}

View File

@ -0,0 +1,3 @@
; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s
; CHECK: error: value has no uses
uselistorder i32* @global, { 1, 0 }

View File

@ -0,0 +1,7 @@
; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s
; CHECK: error: expected distinct uselistorder indexes in range [0, size)
@global = global i32 0
@alias1 = alias i32* @global
@alias2 = alias i32* @global
@alias3 = alias i32* @global
uselistorder i32* @global, { 0, 0, 2 }

View File

@ -0,0 +1,4 @@
; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s
; CHECK: error: value has no uses
@global = global i32 0
uselistorder i32* @global, { 1, 0 }

View File

@ -0,0 +1,5 @@
; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s
; CHECK: error: value only has one use
@global = global i32 0
@alias = alias i32* @global
uselistorder i32* @global, { 1, 0 }

View File

@ -0,0 +1,7 @@
; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s
; CHECK: error: expected uselistorder indexes to change the order
@global = global i32 0
@alias1 = alias i32* @global
@alias2 = alias i32* @global
@alias3 = alias i32* @global
uselistorder i32* @global, { 0, 1, 2 }

View File

@ -0,0 +1,7 @@
; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s
; CHECK: error: expected distinct uselistorder indexes in range [0, size)
@global = global i32 0
@alias1 = alias i32* @global
@alias2 = alias i32* @global
@alias3 = alias i32* @global
uselistorder i32* @global, { 0, 3, 1 }

View File

@ -0,0 +1,7 @@
; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s
; CHECK: error: wrong number of indexes, expected 3
@global = global i32 0
@alias1 = alias i32* @global
@alias2 = alias i32* @global
@alias3 = alias i32* @global
uselistorder i32* @global, { 1, 0 }

View File

@ -0,0 +1,6 @@
; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s
; CHECK: error: wrong number of indexes, expected 2
@global = global i32 0
@alias1 = alias i32* @global
@alias2 = alias i32* @global
uselistorder i32* @global, { 1, 0, 2 }

View File

@ -0,0 +1,4 @@
; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s
; CHECK: error: '@global' defined with type 'i32*'
@global = global i32 0
uselistorder i31* @global, { 1, 0 }

View File

@ -0,0 +1,6 @@
; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s
; CHECK: error: invalid basic block in uselistorder_bb
define void @foo() {
unreachable
}
uselistorder_bb @foo, %bb, { 1, 0 }

View File

@ -0,0 +1,4 @@
; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s
; CHECK: error: invalid declaration in uselistorder_bb
declare void @foo()
uselistorder_bb @foo, %bb, { 1, 0 }

View File

@ -0,0 +1,3 @@
; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s
; CHECK: error: invalid function forward reference in uselistorder_bb
uselistorder_bb @foo, %bb, { 1, 0 }

View File

@ -0,0 +1,6 @@
; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s
; CHECK: error: expected basic block in uselistorder_bb
define i32 @foo(i32 %arg) {
ret i32 %arg
}
uselistorder_bb @foo, %arg, { 1, 0 }

View File

@ -0,0 +1,4 @@
; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s
; CHECK: error: expected function name in uselistorder_bb
@global = global i1 0
uselistorder_bb @global, %bb, { 1, 0 }

View File

@ -0,0 +1,11 @@
; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s
; CHECK: error: invalid numeric label in uselistorder_bb
@ba1 = constant i8* blockaddress (@foo, %1)
define void @foo() {
br label %1
unreachable
}
uselistorder_bb @foo, %1, { 1, 0 }

View File

@ -0,0 +1,56 @@
; RUN: llvm-as < %s -disable-output 2>&1 | FileCheck %s -allow-empty
; CHECK-NOT: error
; CHECK-NOT: warning
; RUN: verify-uselistorder < %s
@a = global [4 x i1] [i1 0, i1 1, i1 0, i1 1]
@b = alias i1* getelementptr ([4 x i1]* @a, i64 0, i64 2)
; Check use-list order of constants used by globals.
@glob1 = global i5 7
@glob2 = global i5 7
@glob3 = global i5 7
define i32 @f32(i32 %a, i32 %b, i32 %c, i32 %d) {
entry:
br label %first
; <label 0>:
%eh = mul i32 %e, %1
%sum = add i32 %eh, %ef
br label %preexit
preexit:
%product = phi i32 [%ef, %first], [%sum, %0]
%backto0 = icmp slt i32 %product, -9
br i1 %backto0, label %0, label %exit
exit:
ret i32 %product
first:
%e = add i32 %a, 7
%f = add i32 %b, 7
%g = add i32 %c, 8
%1 = add i32 %d, 8
%ef = mul i32 %e, %f
%g1 = mul i32 %g, %1
%goto0 = icmp slt i32 %g1, -9
br i1 %goto0, label %0, label %preexit
; uselistorder directives
uselistorder i32 7, { 1, 0 }
uselistorder i32 %1, { 1, 0 }
uselistorder i32 %e, { 1, 0 }
uselistorder label %0, { 1, 0 }
uselistorder label %preexit, { 1, 0 }
}
define i1 @loada() {
entry:
%a = load i1* getelementptr ([4 x i1]* @a, i64 0, i64 2)
ret i1 %a
}
uselistorder i5 7, { 1, 0, 2 }
uselistorder i1* getelementptr ([4 x i1]* @a, i64 0, i64 2), { 1, 0 }

View File

@ -0,0 +1,42 @@
; RUN: llvm-as < %s -disable-output 2>&1 | FileCheck %s -allow-empty
; CHECK-NOT: error
; CHECK-NOT: warning
; RUN: verify-uselistorder < %s
@ba1 = constant i8* blockaddress (@bafunc1, %bb)
@ba2 = constant i8* getelementptr (i8* blockaddress (@bafunc2, %bb), i61 0)
@ba3 = constant i8* getelementptr (i8* blockaddress (@bafunc2, %bb), i61 0)
define i8* @babefore() {
ret i8* getelementptr (i8* blockaddress (@bafunc2, %bb), i61 0)
bb1:
ret i8* blockaddress (@bafunc1, %bb)
bb2:
ret i8* blockaddress (@bafunc3, %bb)
}
define void @bafunc1() {
br label %bb
bb:
unreachable
}
define void @bafunc2() {
br label %bb
bb:
unreachable
}
define void @bafunc3() {
br label %bb
bb:
unreachable
}
define i8* @baafter() {
ret i8* blockaddress (@bafunc2, %bb)
bb1:
ret i8* blockaddress (@bafunc1, %bb)
bb2:
ret i8* blockaddress (@bafunc3, %bb)
}
uselistorder_bb @bafunc1, %bb, { 1, 0 }
uselistorder_bb @bafunc2, %bb, { 1, 0 }
uselistorder_bb @bafunc3, %bb, { 1, 0 }

View File

@ -365,8 +365,7 @@ static void verifyAssemblyUseListOrder(const Module &M) {
static void verifyUseListOrder(const Module &M) {
verifyBitcodeUseListOrder(M);
if (shouldPreserveAssemblyUseListOrder())
verifyAssemblyUseListOrder(M);
verifyAssemblyUseListOrder(M);
}
static void shuffleValueUseLists(Value *V, std::minstd_rand0 &Gen,
@ -532,11 +531,15 @@ int main(int argc, char **argv) {
report_fatal_error("verification failed");
errs() << "*** verify-use-list-order ***\n";
// Can't verify if order isn't preserved.
if (!shouldPreserveBitcodeUseListOrder()) {
// Can't verify if order isn't preserved.
errs() << "warning: forcing -preserve-bc-use-list-order\n";
setPreserveBitcodeUseListOrder(true);
}
if (!shouldPreserveAssemblyUseListOrder()) {
errs() << "warning: forcing -preserve-ll-use-list-order\n";
setPreserveAssemblyUseListOrder(true);
}
// Verify the use lists now and after reversing them.
verifyUseListOrder(*M);

View File

@ -48,6 +48,8 @@
`(,(regexp-opt '("extractelement" "insertelement" "shufflevector") 'words) . font-lock-keyword-face)
;; Aggregate ops
`(,(regexp-opt '("extractvalue" "insertvalue") 'words) . font-lock-keyword-face)
;; Use-list order directives
`(,(regexp-opt '("uselistorder" "uselistorder_bb") 'words) . font-lock-keyword-face)
)
"Syntax highlighting for LLVM"

View File

@ -57,6 +57,7 @@ syn keyword llvmKeyword tail target thread_local to triple unnamed_addr
syn keyword llvmKeyword unordered uwtable volatile weak weak_odr
syn keyword llvmKeyword x86_fastcallcc x86_stdcallcc x86_thiscallcc x86_64_sysvcc
syn keyword llvmKeyword x86_64_win64cc zeroext
syn keyword llvmKeyword uselistorder uselistorder_bb
" Obsolete keywords.
syn keyword llvmError getresult begin end