Allow operations to hold a blocklist and add support for parsing/printing a block list for verbose printing.
PiperOrigin-RevId: 230951462
This commit is contained in:
parent
6d37a255e2
commit
c3424c3c75
|
@ -140,8 +140,18 @@ public:
|
|||
static_cast<SubClass *>(this)->walkPostOrder(it->begin(), it->end());
|
||||
}
|
||||
|
||||
RetTy walkOpInst(OperationInst *opInst) {
|
||||
return static_cast<SubClass *>(this)->visitOperationInst(opInst);
|
||||
void walkOpInst(OperationInst *opInst) {
|
||||
static_cast<SubClass *>(this)->visitOperationInst(opInst);
|
||||
for (auto &blockList : opInst->getBlockLists())
|
||||
for (auto &block : blockList)
|
||||
static_cast<SubClass *>(this)->walk(block.begin(), block.end());
|
||||
}
|
||||
|
||||
void walkOpInstPostOrder(OperationInst *opInst) {
|
||||
for (auto &blockList : opInst->getBlockLists())
|
||||
for (auto &block : blockList)
|
||||
static_cast<SubClass *>(this)->walk(block.begin(), block.end());
|
||||
static_cast<SubClass *>(this)->visitOperationInst(opInst);
|
||||
}
|
||||
|
||||
void walkForInst(ForInst *forInst) {
|
||||
|
@ -204,7 +214,8 @@ public:
|
|||
return static_cast<SubClass *>(this)->walkIfInstPostOrder(
|
||||
cast<IfInst>(s));
|
||||
case Instruction::Kind::OperationInst:
|
||||
return static_cast<SubClass *>(this)->walkOpInst(cast<OperationInst>(s));
|
||||
return static_cast<SubClass *>(this)->walkOpInstPostOrder(
|
||||
cast<OperationInst>(s));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -48,13 +48,15 @@ class Function;
|
|||
class OperationInst final
|
||||
: public Instruction,
|
||||
private llvm::TrailingObjects<OperationInst, InstResult, BlockOperand,
|
||||
unsigned, InstOperand> {
|
||||
unsigned, InstOperand, BlockList> {
|
||||
public:
|
||||
/// Create a new OperationInst with the specific fields.
|
||||
static OperationInst *
|
||||
create(Location location, OperationName name, ArrayRef<Value *> operands,
|
||||
ArrayRef<Type> resultTypes, ArrayRef<NamedAttribute> attributes,
|
||||
ArrayRef<Block *> successors, MLIRContext *context);
|
||||
static OperationInst *create(Location location, OperationName name,
|
||||
ArrayRef<Value *> operands,
|
||||
ArrayRef<Type> resultTypes,
|
||||
ArrayRef<NamedAttribute> attributes,
|
||||
ArrayRef<Block *> successors,
|
||||
unsigned numBlockLists, MLIRContext *context);
|
||||
|
||||
/// Return the context this operation is associated with.
|
||||
MLIRContext *getContext() const;
|
||||
|
@ -226,6 +228,30 @@ public:
|
|||
/// value indicates whether the attribute was present or not.
|
||||
RemoveResult removeAttr(Identifier name);
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Blocks
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
/// Returns the number of block lists held by this operation.
|
||||
unsigned getNumBlockLists() const { return numBlockLists; }
|
||||
|
||||
/// Returns the block lists held by this operation.
|
||||
MutableArrayRef<BlockList> getBlockLists() {
|
||||
return {getTrailingObjects<BlockList>(), numBlockLists};
|
||||
}
|
||||
ArrayRef<BlockList> getBlockLists() const {
|
||||
return const_cast<OperationInst *>(this)->getBlockLists();
|
||||
}
|
||||
|
||||
/// Returns the block list held by this operation at position 'index'.
|
||||
BlockList &getBlockList(unsigned index) {
|
||||
assert(index < numBlockLists && "invalid block list index");
|
||||
return getBlockLists()[index];
|
||||
}
|
||||
const BlockList &getBlockList(unsigned index) const {
|
||||
return const_cast<OperationInst *>(this)->getBlockList(index);
|
||||
}
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Terminators
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
@ -404,7 +430,7 @@ public:
|
|||
|
||||
private:
|
||||
unsigned numOperands;
|
||||
const unsigned numResults, numSuccs;
|
||||
const unsigned numResults, numSuccs, numBlockLists;
|
||||
|
||||
/// This holds the name of the operation.
|
||||
OperationName name;
|
||||
|
@ -414,7 +440,8 @@ private:
|
|||
|
||||
OperationInst(Location location, OperationName name, unsigned numOperands,
|
||||
unsigned numResults, unsigned numSuccessors,
|
||||
ArrayRef<NamedAttribute> attributes, MLIRContext *context);
|
||||
unsigned numBlockLists, ArrayRef<NamedAttribute> attributes,
|
||||
MLIRContext *context);
|
||||
~OperationInst();
|
||||
|
||||
/// Erase the operand at 'index'.
|
||||
|
@ -422,7 +449,7 @@ private:
|
|||
|
||||
// This stuff is used by the TrailingObjects template.
|
||||
friend llvm::TrailingObjects<OperationInst, InstResult, BlockOperand,
|
||||
unsigned, InstOperand>;
|
||||
unsigned, InstOperand, BlockList>;
|
||||
size_t numTrailingObjects(OverloadToken<InstOperand>) const {
|
||||
return numOperands;
|
||||
}
|
||||
|
@ -432,6 +459,9 @@ private:
|
|||
size_t numTrailingObjects(OverloadToken<BlockOperand>) const {
|
||||
return numSuccs;
|
||||
}
|
||||
size_t numTrailingObjects(OverloadToken<BlockList>) const {
|
||||
return numBlockLists;
|
||||
}
|
||||
size_t numTrailingObjects(OverloadToken<unsigned>) const { return numSuccs; }
|
||||
};
|
||||
|
||||
|
|
|
@ -233,6 +233,7 @@ struct OperationState {
|
|||
SmallVector<NamedAttribute, 4> attributes;
|
||||
/// Successors of this operation and their respective operands.
|
||||
SmallVector<Block *, 1> successors;
|
||||
unsigned numBlockLists = 0;
|
||||
|
||||
public:
|
||||
OperationState(MLIRContext *context, Location location, StringRef name)
|
||||
|
@ -244,12 +245,13 @@ public:
|
|||
OperationState(MLIRContext *context, Location location, StringRef name,
|
||||
ArrayRef<Value *> operands, ArrayRef<Type> types,
|
||||
ArrayRef<NamedAttribute> attributes,
|
||||
ArrayRef<Block *> successors = {})
|
||||
ArrayRef<Block *> successors = {}, unsigned numBlockLists = 0)
|
||||
: context(context), location(location), name(name, context),
|
||||
operands(operands.begin(), operands.end()),
|
||||
types(types.begin(), types.end()),
|
||||
attributes(attributes.begin(), attributes.end()),
|
||||
successors(successors.begin(), successors.end()) {}
|
||||
successors(successors.begin(), successors.end()),
|
||||
numBlockLists(numBlockLists) {}
|
||||
|
||||
void addOperands(ArrayRef<Value *> newOperands) {
|
||||
assert(successors.empty() &&
|
||||
|
@ -277,6 +279,9 @@ public:
|
|||
operands.push_back(nullptr);
|
||||
operands.append(succOperands.begin(), succOperands.end());
|
||||
}
|
||||
|
||||
/// Add a new block list with the specified blocks.
|
||||
void reserveBlockLists(unsigned numReserved) { numBlockLists += numReserved; }
|
||||
};
|
||||
|
||||
} // end namespace mlir
|
||||
|
|
|
@ -365,6 +365,14 @@ static Instruction *getInstAtPosition(ArrayRef<unsigned> positions,
|
|||
if (auto *elseClause = ifInst->getElse())
|
||||
return getInstAtPosition(positions, level + 1, elseClause);
|
||||
}
|
||||
if (auto *opInst = dyn_cast<OperationInst>(&inst)) {
|
||||
for (auto &blockList : opInst->getBlockLists()) {
|
||||
for (auto &b : blockList)
|
||||
if (auto *ret = getInstAtPosition(positions, level + 1, &b))
|
||||
return ret;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -165,11 +165,6 @@ bool FuncVerifier::verifyAttribute(Attribute attr, const OperationInst &op) {
|
|||
}
|
||||
|
||||
bool FuncVerifier::verifyBlock(const Block &block, bool isTopLevel) {
|
||||
// Blocks under IfInst/ForInst don't have terminators, but blocks at the top
|
||||
// level of a function do.
|
||||
if (isTopLevel && !block.getTerminator())
|
||||
return failure("block with no terminator", block);
|
||||
|
||||
for (auto *arg : block.getArguments()) {
|
||||
if (arg->getOwner() != &block)
|
||||
return failure("block argument not owned by block", block);
|
||||
|
@ -191,6 +186,25 @@ bool FuncVerifier::verifyBlock(const Block &block, bool isTopLevel) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If this block is at the function level, then verify that it has a
|
||||
// terminator.
|
||||
if (isTopLevel) {
|
||||
if (!block.getTerminator())
|
||||
return failure("block with no terminator", block);
|
||||
|
||||
// Verify that this block is not branching to a block of a different
|
||||
// region.
|
||||
for (const Block *successor : block.getSuccessors())
|
||||
if (successor->getParent() != block.getParent())
|
||||
return failure("branching to a block of a different region",
|
||||
*block.getTerminator());
|
||||
} else if (block.getTerminator()) {
|
||||
// TODO(riverriddle) Blocks in an IfInst/ForInst aren't allowed to have
|
||||
// terminators.
|
||||
return failure("non function block with terminator", block);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -222,6 +236,12 @@ bool FuncVerifier::verifyOperation(const OperationInst &op) {
|
|||
return true;
|
||||
}
|
||||
|
||||
// Verify that all child blocks are ok.
|
||||
for (auto &blockList : op.getBlockLists())
|
||||
for (auto &b : blockList)
|
||||
if (verifyBlock(b, /*isTopLevel=*/false))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -249,10 +269,16 @@ bool FuncVerifier::verifyDominance(const Block &block) {
|
|||
return true;
|
||||
|
||||
switch (inst.getKind()) {
|
||||
case Instruction::Kind::OperationInst:
|
||||
if (verifyOperation(cast<OperationInst>(inst)))
|
||||
case Instruction::Kind::OperationInst: {
|
||||
auto &opInst = cast<OperationInst>(inst);
|
||||
if (verifyOperation(opInst))
|
||||
return true;
|
||||
for (auto &blockList : opInst.getBlockLists())
|
||||
for (auto &block : blockList)
|
||||
if (verifyDominance(block))
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
case Instruction::Kind::For:
|
||||
if (verifyDominance(*cast<ForInst>(inst).getBody()))
|
||||
return true;
|
||||
|
|
|
@ -1095,6 +1095,14 @@ public:
|
|||
void printSuccessorAndUseList(const OperationInst *term,
|
||||
unsigned index) override;
|
||||
|
||||
/// Print a block list.
|
||||
void printBlockList(const BlockList &blocks) {
|
||||
os << " {\n";
|
||||
for (auto &b : blocks)
|
||||
print(&b);
|
||||
os.indent(currentIndent) << "}";
|
||||
}
|
||||
|
||||
// Print if and loop bounds.
|
||||
void printDimAndSymbolList(ArrayRef<InstOperand> ops, unsigned numDims);
|
||||
void printBound(AffineBound bound, const char *prefix);
|
||||
|
@ -1163,6 +1171,9 @@ void FunctionPrinter::numberValuesInBlock(const Block &block) {
|
|||
auto *opInst = cast<OperationInst>(&inst);
|
||||
if (opInst->getNumResults() != 0)
|
||||
numberValueID(opInst->getResult(0));
|
||||
for (auto &blockList : opInst->getBlockLists())
|
||||
for (const auto &block : blockList)
|
||||
numberValuesInBlock(block);
|
||||
break;
|
||||
}
|
||||
case Instruction::Kind::For: {
|
||||
|
@ -1540,6 +1551,10 @@ void FunctionPrinter::printGenericOp(const OperationInst *op) {
|
|||
[&](const Value *result) { printType(result->getType()); });
|
||||
os << ')';
|
||||
}
|
||||
|
||||
// Print any trailing block lists.
|
||||
for (auto &blockList : op->getBlockLists())
|
||||
printBlockList(blockList);
|
||||
}
|
||||
|
||||
void FunctionPrinter::printSuccessorAndUseList(const OperationInst *term,
|
||||
|
|
|
@ -264,7 +264,7 @@ void BlockList::cloneInto(BlockList *dest, BlockAndValueMapping &mapper,
|
|||
if (empty())
|
||||
return;
|
||||
|
||||
Block *lastOldBlock = &dest->back();
|
||||
iterator lastOldBlock = --dest->end();
|
||||
for (const Block &block : *this) {
|
||||
Block *newBlock = new Block();
|
||||
mapper.map(&block, newBlock);
|
||||
|
@ -306,8 +306,7 @@ void BlockList::cloneInto(BlockList *dest, BlockAndValueMapping &mapper,
|
|||
};
|
||||
|
||||
Walker v(mapper);
|
||||
for (auto it = std::next(lastOldBlock->getIterator()), e = dest->end();
|
||||
it != e; ++it)
|
||||
for (auto it = std::next(lastOldBlock), e = dest->end(); it != e; ++it)
|
||||
v.walk(it->begin(), it->end());
|
||||
}
|
||||
|
||||
|
|
|
@ -305,9 +305,9 @@ Block *FuncBuilder::createBlock(Block *insertBefore) {
|
|||
|
||||
/// Create an operation given the fields represented as an OperationState.
|
||||
OperationInst *FuncBuilder::createOperation(const OperationState &state) {
|
||||
auto *op = OperationInst::create(state.location, state.name, state.operands,
|
||||
state.types, state.attributes,
|
||||
state.successors, context);
|
||||
auto *op = OperationInst::create(
|
||||
state.location, state.name, state.operands, state.types, state.attributes,
|
||||
state.successors, state.numBlockLists, context);
|
||||
block->getInstructions().insert(insertPoint, op);
|
||||
return op;
|
||||
}
|
||||
|
|
|
@ -296,12 +296,17 @@ void Instruction::dropAllReferences() {
|
|||
elseBlock->dropAllReferences();
|
||||
break;
|
||||
}
|
||||
case Kind::OperationInst:
|
||||
case Kind::OperationInst: {
|
||||
auto *opInst = cast<OperationInst>(this);
|
||||
if (isTerminator())
|
||||
for (auto &dest : cast<OperationInst>(this)->getBlockOperands())
|
||||
for (auto &dest : opInst->getBlockOperands())
|
||||
dest.drop();
|
||||
for (auto &blockList : opInst->getBlockLists())
|
||||
for (Block &block : blockList)
|
||||
block.dropAllReferences();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -314,6 +319,7 @@ OperationInst *OperationInst::create(Location location, OperationName name,
|
|||
ArrayRef<Type> resultTypes,
|
||||
ArrayRef<NamedAttribute> attributes,
|
||||
ArrayRef<Block *> successors,
|
||||
unsigned numBlockLists,
|
||||
MLIRContext *context) {
|
||||
unsigned numSuccessors = successors.size();
|
||||
|
||||
|
@ -322,18 +328,23 @@ OperationInst *OperationInst::create(Location location, OperationName name,
|
|||
unsigned numOperands = operands.size() - numSuccessors;
|
||||
|
||||
auto byteSize =
|
||||
totalSizeToAlloc<InstResult, BlockOperand, unsigned, InstOperand>(
|
||||
resultTypes.size(), numSuccessors, numSuccessors, numOperands);
|
||||
totalSizeToAlloc<InstResult, BlockOperand, unsigned, InstOperand,
|
||||
BlockList>(resultTypes.size(), numSuccessors,
|
||||
numSuccessors, numOperands, numBlockLists);
|
||||
void *rawMem = malloc(byteSize);
|
||||
|
||||
// Initialize the OperationInst part of the instruction.
|
||||
auto inst = ::new (rawMem)
|
||||
OperationInst(location, name, numOperands, resultTypes.size(),
|
||||
numSuccessors, attributes, context);
|
||||
numSuccessors, numBlockLists, attributes, context);
|
||||
|
||||
assert((numSuccessors == 0 || inst->isTerminator()) &&
|
||||
"unexpected successors in a non-terminator operation");
|
||||
|
||||
// Initialize the block lists.
|
||||
for (unsigned i = 0; i != numBlockLists; ++i)
|
||||
new (&inst->getBlockList(i)) BlockList(inst);
|
||||
|
||||
// Initialize the results and operands.
|
||||
auto instResults = inst->getInstResults();
|
||||
for (unsigned i = 0, e = resultTypes.size(); i != e; ++i)
|
||||
|
@ -401,11 +412,12 @@ OperationInst *OperationInst::create(Location location, OperationName name,
|
|||
|
||||
OperationInst::OperationInst(Location location, OperationName name,
|
||||
unsigned numOperands, unsigned numResults,
|
||||
unsigned numSuccessors,
|
||||
unsigned numSuccessors, unsigned numBlockLists,
|
||||
ArrayRef<NamedAttribute> attributes,
|
||||
MLIRContext *context)
|
||||
: Instruction(Kind::OperationInst, location), numOperands(numOperands),
|
||||
numResults(numResults), numSuccs(numSuccessors), name(name) {
|
||||
numResults(numResults), numSuccs(numSuccessors),
|
||||
numBlockLists(numBlockLists), name(name) {
|
||||
#ifndef NDEBUG
|
||||
for (auto elt : attributes)
|
||||
assert(elt.second != nullptr && "Attributes cannot have null entries");
|
||||
|
@ -426,6 +438,10 @@ OperationInst::~OperationInst() {
|
|||
if (isTerminator())
|
||||
for (auto &successor : getBlockOperands())
|
||||
successor.~BlockOperand();
|
||||
|
||||
// Explicitly destroy the block list.
|
||||
for (auto &blockList : getBlockLists())
|
||||
blockList.~BlockList();
|
||||
}
|
||||
|
||||
/// Return true if there are no users of any results of this operation.
|
||||
|
@ -860,9 +876,17 @@ Instruction *Instruction::clone(BlockAndValueMapping &mapper,
|
|||
resultTypes.reserve(opInst->getNumResults());
|
||||
for (auto *result : opInst->getResults())
|
||||
resultTypes.push_back(result->getType());
|
||||
|
||||
unsigned numBlockLists = opInst->getNumBlockLists();
|
||||
auto *newOp = OperationInst::create(getLoc(), opInst->getName(), operands,
|
||||
resultTypes, opInst->getAttrs(),
|
||||
successors, context);
|
||||
successors, numBlockLists, context);
|
||||
|
||||
// Clone the block lists.
|
||||
for (unsigned i = 0; i != numBlockLists; ++i)
|
||||
opInst->getBlockList(i).cloneInto(&newOp->getBlockList(i), mapper,
|
||||
context);
|
||||
|
||||
// Remember the mapping of any results.
|
||||
for (unsigned i = 0, e = opInst->getNumResults(); i != e; ++i)
|
||||
mapper.map(opInst->getResult(i), newOp->getResult(i));
|
||||
|
|
|
@ -2130,9 +2130,19 @@ public:
|
|||
parseOptionalBlockArgList(SmallVectorImpl<BlockArgument *> &results,
|
||||
Block *owner);
|
||||
|
||||
ParseResult parseBlock(Block *blockToUse);
|
||||
ParseResult parseOperationBlockList(SmallVectorImpl<Block *> &results);
|
||||
ParseResult parseBlockListBody(SmallVectorImpl<Block *> &results);
|
||||
ParseResult parseBlock(Block *&block);
|
||||
ParseResult parseBlockBody(Block *block);
|
||||
|
||||
/// Cleans up the memory for allocated blocks when a parser error occurs.
|
||||
void cleanupInvalidBlocks(ArrayRef<Block *> invalidBlocks) {
|
||||
// Add the referenced blocks to the function so that they can be properly
|
||||
// cleaned up when the function is destroyed.
|
||||
for (auto *block : invalidBlocks)
|
||||
function->push_back(block);
|
||||
}
|
||||
|
||||
/// After the function is finished parsing, this function checks to see if
|
||||
/// there are any remaining issues.
|
||||
ParseResult finalizeFunction(SMLoc loc);
|
||||
|
@ -2243,21 +2253,24 @@ ParseResult FunctionParser::parseFunctionBody(bool hadNamedArguments) {
|
|||
// The first block is already created and should be filled in.
|
||||
auto firstBlock = &function->front();
|
||||
|
||||
// Parse the remaining list of blocks.
|
||||
while (!consumeIf(Token::r_brace)) {
|
||||
if (parseBlock(firstBlock))
|
||||
return ParseFailure;
|
||||
// Parse the first block.
|
||||
if (parseBlock(firstBlock))
|
||||
return ParseFailure;
|
||||
|
||||
// Create the second and subsequent block.
|
||||
firstBlock = nullptr;
|
||||
}
|
||||
// Parse the remaining list of blocks.
|
||||
SmallVector<Block *, 16> blocks;
|
||||
if (parseBlockListBody(blocks))
|
||||
return ParseFailure;
|
||||
function->getBlocks().insert(function->end(), blocks.begin(), blocks.end());
|
||||
|
||||
// Verify that all referenced blocks were defined.
|
||||
if (!forwardRef.empty()) {
|
||||
SmallVector<std::pair<const char *, Block *>, 4> errors;
|
||||
// Iteration over the map isn't deterministic, so sort by source location.
|
||||
for (auto entry : forwardRef)
|
||||
for (auto entry : forwardRef) {
|
||||
errors.push_back({entry.second.getPointer(), entry.first});
|
||||
cleanupInvalidBlocks(entry.first);
|
||||
}
|
||||
llvm::array_pod_sort(errors.begin(), errors.end());
|
||||
|
||||
for (auto entry : errors) {
|
||||
|
@ -2270,6 +2283,55 @@ ParseResult FunctionParser::parseFunctionBody(bool hadNamedArguments) {
|
|||
return finalizeFunction(braceLoc);
|
||||
}
|
||||
|
||||
/// Block list.
|
||||
///
|
||||
/// block-list ::= '{' block-list-body
|
||||
///
|
||||
ParseResult
|
||||
FunctionParser::parseOperationBlockList(SmallVectorImpl<Block *> &results) {
|
||||
// Parse the '{'.
|
||||
if (parseToken(Token::l_brace, "expected '{' to begin block list"))
|
||||
return ParseFailure;
|
||||
// Check for an empty block list.
|
||||
if (consumeIf(Token::r_brace))
|
||||
return ParseSuccess;
|
||||
Block *currentBlock = builder.getInsertionBlock();
|
||||
|
||||
// Parse the first block directly to allow for it to be unnamed.
|
||||
Block *block = new Block();
|
||||
if (parseBlock(block)) {
|
||||
cleanupInvalidBlocks(block);
|
||||
return ParseFailure;
|
||||
}
|
||||
results.push_back(block);
|
||||
|
||||
// Parse the rest of the block list.
|
||||
if (parseBlockListBody(results))
|
||||
return ParseFailure;
|
||||
|
||||
// Reset insertion point to the current block.
|
||||
builder.setInsertionPointToEnd(currentBlock);
|
||||
return ParseSuccess;
|
||||
}
|
||||
|
||||
/// Block list.
|
||||
///
|
||||
/// block-list-body ::= block* '}'
|
||||
///
|
||||
ParseResult
|
||||
FunctionParser::parseBlockListBody(SmallVectorImpl<Block *> &results) {
|
||||
// Parse the block list.
|
||||
while (!consumeIf(Token::r_brace)) {
|
||||
Block *newBlock = nullptr;
|
||||
if (parseBlock(newBlock)) {
|
||||
cleanupInvalidBlocks(results);
|
||||
return ParseFailure;
|
||||
}
|
||||
results.push_back(newBlock);
|
||||
}
|
||||
return ParseSuccess;
|
||||
}
|
||||
|
||||
/// Block declaration.
|
||||
///
|
||||
/// block ::= block-label? instruction* terminator-inst
|
||||
|
@ -2277,9 +2339,7 @@ ParseResult FunctionParser::parseFunctionBody(bool hadNamedArguments) {
|
|||
/// block-id ::= caret-id
|
||||
/// block-arg-list ::= `(` ssa-id-and-type-list? `)`
|
||||
///
|
||||
ParseResult FunctionParser::parseBlock(Block *blockToUse) {
|
||||
Block *block = blockToUse;
|
||||
|
||||
ParseResult FunctionParser::parseBlock(Block *&block) {
|
||||
// The first block for a function is already created.
|
||||
if (block) {
|
||||
// The name for a first block is optional.
|
||||
|
@ -2348,10 +2408,9 @@ Value *FunctionParser::createForwardReferencePlaceholder(SMLoc loc, Type type) {
|
|||
// cannot be created through normal user input, allowing us to distinguish
|
||||
// them.
|
||||
auto name = OperationName("placeholder", getContext());
|
||||
auto *inst = OperationInst::create(getEncodedSourceLocation(loc), name,
|
||||
/*operands=*/{}, type,
|
||||
/*attributes=*/{},
|
||||
/*successors=*/{}, getContext());
|
||||
auto *inst = OperationInst::create(
|
||||
getEncodedSourceLocation(loc), name, /*operands=*/{}, type,
|
||||
/*attributes=*/{}, /*successors=*/{}, /*numBlockLists=*/0, getContext());
|
||||
forwardReferencePlaceholders[inst->getResult(0)] = loc;
|
||||
return inst->getResult(0);
|
||||
}
|
||||
|
@ -2559,7 +2618,6 @@ Block *FunctionParser::getBlockNamed(StringRef name, SMLoc loc) {
|
|||
if (!blockAndLoc.first) {
|
||||
blockAndLoc.first = new Block();
|
||||
forwardRef[blockAndLoc.first] = loc;
|
||||
function->push_back(blockAndLoc.first);
|
||||
blockAndLoc.second = loc;
|
||||
}
|
||||
|
||||
|
@ -2574,7 +2632,7 @@ Block *FunctionParser::defineBlockNamed(StringRef name, SMLoc loc,
|
|||
if (!blockAndLoc.first) {
|
||||
// If the caller provided a block, use it. Otherwise create a new one.
|
||||
if (!existing)
|
||||
existing = builder.createBlock();
|
||||
existing = new Block();
|
||||
blockAndLoc.first = existing;
|
||||
blockAndLoc.second = loc;
|
||||
return blockAndLoc.first;
|
||||
|
@ -2585,11 +2643,6 @@ Block *FunctionParser::defineBlockNamed(StringRef name, SMLoc loc,
|
|||
// redeclaration.
|
||||
if (!forwardRef.erase(blockAndLoc.first))
|
||||
return nullptr;
|
||||
|
||||
// Move the block to the end of the function. Forward ref'd blocks are
|
||||
// inserted wherever they happen to be referenced.
|
||||
function->getBlocks().splice(function->end(), function->getBlocks(),
|
||||
blockAndLoc.first);
|
||||
return blockAndLoc.first;
|
||||
}
|
||||
|
||||
|
@ -2706,17 +2759,6 @@ ParseResult FunctionParser::parseOperation() {
|
|||
if (!op)
|
||||
return ParseFailure;
|
||||
|
||||
// We just parsed an operation. If it is a recognized one, verify that it
|
||||
// is structurally as we expect. If not, produce an error with a reasonable
|
||||
// source location.
|
||||
if (auto *opInfo = op->getAbstractOperation()) {
|
||||
// We don't wan't to verify branching terminators at this time because
|
||||
// the successors may not have been fully parsed yet.
|
||||
if (!(op->isTerminator() && op->getNumSuccessors() != 0) &&
|
||||
opInfo->verifyInvariants(op))
|
||||
return ParseFailure;
|
||||
}
|
||||
|
||||
// If the instruction had a name, register it.
|
||||
if (!resultID.empty()) {
|
||||
if (op->getNumResults() == 0)
|
||||
|
@ -2813,7 +2855,27 @@ OperationInst *FunctionParser::parseGenericOperation() {
|
|||
result.addSuccessor(successor, operands);
|
||||
}
|
||||
|
||||
return builder.createOperation(result);
|
||||
// Parse the optional block lists for this operation.
|
||||
std::vector<SmallVector<Block *, 2>> blocks;
|
||||
while (getToken().is(Token::l_brace)) {
|
||||
SmallVector<Block *, 2> newBlocks;
|
||||
if (parseOperationBlockList(newBlocks)) {
|
||||
for (auto &blockList : blocks)
|
||||
cleanupInvalidBlocks(blockList);
|
||||
return nullptr;
|
||||
}
|
||||
blocks.emplace_back(newBlocks);
|
||||
}
|
||||
result.reserveBlockLists(blocks.size());
|
||||
|
||||
auto *opInst = builder.createOperation(result);
|
||||
|
||||
// Initialize the parsed block lists.
|
||||
for (unsigned i = 0, e = blocks.size(); i != e; ++i) {
|
||||
auto &blockList = opInst->getBlockList(i).getBlocks();
|
||||
blockList.insert(blockList.end(), blocks[i].begin(), blocks[i].end());
|
||||
}
|
||||
return opInst;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
@ -3146,8 +3208,9 @@ ParseResult FunctionParser::parseForInst() {
|
|||
// If parsing of the for instruction body fails,
|
||||
// MLIR contains for instruction with those nested instructions that have been
|
||||
// successfully parsed.
|
||||
auto *forBody = forInst->getBody();
|
||||
if (parseToken(Token::l_brace, "expected '{' before instruction list") ||
|
||||
parseBlock(forInst->getBody()) ||
|
||||
parseBlock(forBody) ||
|
||||
parseToken(Token::r_brace, "expected '}' after instruction list"))
|
||||
return ParseFailure;
|
||||
|
||||
|
|
|
@ -109,8 +109,9 @@ struct CSE : public FunctionPass {
|
|||
bool processed;
|
||||
};
|
||||
|
||||
/// Attempt to eliminate a redundant operation.
|
||||
void simplifyOperation(OperationInst *op);
|
||||
/// Attempt to eliminate a redundant operation. Returns true if the operation
|
||||
/// was marked for removal, false otherwise.
|
||||
bool simplifyOperation(OperationInst *op);
|
||||
|
||||
void simplifyBlock(Block *bb);
|
||||
|
||||
|
@ -128,16 +129,16 @@ private:
|
|||
char CSE::passID = 0;
|
||||
|
||||
/// Attempt to eliminate a redundant operation.
|
||||
void CSE::simplifyOperation(OperationInst *op) {
|
||||
bool CSE::simplifyOperation(OperationInst *op) {
|
||||
// TODO(riverriddle) We currently only eliminate non side-effecting
|
||||
// operations.
|
||||
if (!op->hasNoSideEffect())
|
||||
return;
|
||||
return false;
|
||||
|
||||
// If the operation is already trivially dead just add it to the erase list.
|
||||
if (op->use_empty()) {
|
||||
opsToErase.push_back(op);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Look for an existing definition for the operation.
|
||||
|
@ -155,18 +156,33 @@ void CSE::simplifyOperation(OperationInst *op) {
|
|||
!op->getLoc().isa<UnknownLoc>()) {
|
||||
existing->setLoc(op->getLoc());
|
||||
}
|
||||
} else {
|
||||
// Otherwise, we add this operation to the known values map.
|
||||
knownValues.insert(op, op);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, we add this operation to the known values map.
|
||||
knownValues.insert(op, op);
|
||||
return false;
|
||||
}
|
||||
|
||||
void CSE::simplifyBlock(Block *bb) {
|
||||
for (auto &i : *bb) {
|
||||
switch (i.getKind()) {
|
||||
case Instruction::Kind::OperationInst:
|
||||
simplifyOperation(&cast<OperationInst>(i));
|
||||
case Instruction::Kind::OperationInst: {
|
||||
auto *opInst = cast<OperationInst>(&i);
|
||||
|
||||
// If the operation is simplified, we don't process any held block lists.
|
||||
if (simplifyOperation(opInst))
|
||||
continue;
|
||||
|
||||
// Simplify any held blocks.
|
||||
for (auto &blockList : opInst->getBlockLists()) {
|
||||
for (auto &b : blockList) {
|
||||
ScopedMapTy::ScopeTy scope(knownValues);
|
||||
simplifyBlock(&b);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Instruction::Kind::For: {
|
||||
ScopedMapTy::ScopeTy scope(knownValues);
|
||||
simplifyBlock(cast<ForInst>(i).getBody());
|
||||
|
|
|
@ -120,7 +120,13 @@ PassResult LoopUnroll::runOnFunction(Function *f) {
|
|||
return hasInnerLoops;
|
||||
}
|
||||
|
||||
bool visitOperationInst(OperationInst *opInst) { return false; }
|
||||
bool walkOpInstPostOrder(OperationInst *opInst) {
|
||||
for (auto &blockList : opInst->getBlockLists())
|
||||
for (auto &block : blockList)
|
||||
if (walkPostOrder(block.begin(), block.end()))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME: can't use base class method for this because that in turn would
|
||||
// need to use the derived class method above. CRTP doesn't allow it, and
|
||||
|
|
|
@ -416,6 +416,9 @@ instantiate(FuncBuilder *b, OperationInst *opInst, VectorType hwVectorType,
|
|||
"Should call the function specialized for VectorTransferReadOp");
|
||||
assert(!opInst->isa<VectorTransferWriteOp>() &&
|
||||
"Should call the function specialized for VectorTransferWriteOp");
|
||||
if (opInst->getNumBlockLists() != 0)
|
||||
return nullptr;
|
||||
|
||||
bool fail = false;
|
||||
auto operands = map(
|
||||
[hwVectorType, substitutionsMap, &fail](Value *v) -> Value * {
|
||||
|
|
|
@ -1097,6 +1097,8 @@ static OperationInst *vectorizeOneOperationInst(FuncBuilder *b,
|
|||
opInst->erase();
|
||||
return res;
|
||||
}
|
||||
if (opInst->getNumBlockLists() != 0)
|
||||
return nullptr;
|
||||
|
||||
auto types = map([state](Value *v) { return getVectorType(v, *state); },
|
||||
opInst->getResults());
|
||||
|
|
|
@ -186,7 +186,7 @@ func @func_with_ops(i32) {
|
|||
func @func_with_ops(i32) {
|
||||
^bb0(%a : i32):
|
||||
// expected-error@+1 {{'predicate' attribute value out of range}}
|
||||
%r = "cmpi"(%a, %b) {predicate: 42} : (i32, i32) -> i1
|
||||
%r = "cmpi"(%a, %a) {predicate: 42} : (i32, i32) -> i1
|
||||
}
|
||||
|
||||
// -----
|
||||
|
|
|
@ -488,6 +488,7 @@ func @return_inside_loop() -> i8 {
|
|||
return %a : i8
|
||||
// expected-error@-1 {{'return' op may only be at the top level of a function}}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// -----
|
||||
|
|
|
@ -747,3 +747,39 @@ func @type_alias() -> !i32_type_alias {
|
|||
%0 = "foo"() : () -> i32
|
||||
return %0 : i32
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @verbose_if(
|
||||
func @verbose_if(%N: index) {
|
||||
%c = constant 200 : index
|
||||
|
||||
// CHECK: "if"(%c200, %arg0, %c200) {cond: #set0} : (index, index, index) -> () {
|
||||
"if"(%c, %N, %c) { cond: #set0 } : (index, index, index) -> () {
|
||||
// CHECK-NEXT: "add"
|
||||
%y = "add"(%c, %N) : (index, index) -> index
|
||||
// CHECK-NEXT: } {
|
||||
} { // The else block list.
|
||||
// CHECK-NEXT: "add"
|
||||
%z = "add"(%c, %c) : (index, index) -> index
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @verbose_for
|
||||
func @verbose_for(%arg0 : index, %arg1 : index) {
|
||||
// CHECK-NEXT: %0 = "for"() {lb: 1, ub: 10} : () -> index {
|
||||
%a = "for"() {lb: 1, ub: 10 } : () -> index {
|
||||
|
||||
// CHECK-NEXT: %1 = "for"() {lb: 1, step: 2, ub: 100} : () -> index {
|
||||
%b = "for"() {lb: 1, ub: 100, step: 2 } : () -> index {
|
||||
|
||||
// CHECK-NEXT: %2 = "for"(%arg0, %arg1) : (index, index) -> index {
|
||||
%c = "for"(%arg0, %arg1) : (index, index) -> index {
|
||||
|
||||
// CHECK-NEXT: %3 = "for"(%arg0) {ub: 100} : (index) -> index {
|
||||
%d = "for"(%arg0) {ub: 100 } : (index) -> index {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue