forked from OSchip/llvm-project
Replace TerminatorInst with builtin terminator operations.
Note: Terminators will be merged into the operations list in a follow up patch. PiperOrigin-RevId: 221670037
This commit is contained in:
parent
de828dd259
commit
503caf0722
|
@ -116,9 +116,9 @@ public:
|
|||
//===--------------------------------------------------------------------===//
|
||||
|
||||
/// Change the terminator of this block to the specified instruction.
|
||||
void setTerminator(TerminatorInst *inst);
|
||||
void setTerminator(OperationInst *inst);
|
||||
|
||||
TerminatorInst *getTerminator() const { return terminator; }
|
||||
OperationInst *getTerminator() const { return terminator; }
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Predecessors and successors.
|
||||
|
@ -217,7 +217,7 @@ private:
|
|||
std::vector<BBArgument *> arguments;
|
||||
|
||||
/// This is the owning reference to the terminator of the block.
|
||||
TerminatorInst *terminator = nullptr;
|
||||
OperationInst *terminator = nullptr;
|
||||
|
||||
BasicBlock(const BasicBlock&) = delete;
|
||||
void operator=(const BasicBlock&) = delete;
|
||||
|
|
|
@ -267,32 +267,7 @@ public:
|
|||
return op;
|
||||
}
|
||||
|
||||
// Terminators.
|
||||
|
||||
ReturnInst *createReturn(Location location, ArrayRef<CFGValue *> operands) {
|
||||
return insertTerminator(ReturnInst::create(location, operands));
|
||||
}
|
||||
|
||||
BranchInst *createBranch(Location location, BasicBlock *dest,
|
||||
ArrayRef<CFGValue *> operands = {}) {
|
||||
return insertTerminator(BranchInst::create(location, dest, operands));
|
||||
}
|
||||
|
||||
CondBranchInst *createCondBranch(Location location, CFGValue *condition,
|
||||
BasicBlock *trueDest,
|
||||
BasicBlock *falseDest) {
|
||||
return insertTerminator(
|
||||
CondBranchInst::create(location, condition, trueDest, falseDest));
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T> T *insertTerminator(T *term) {
|
||||
// FIXME: b/118738403
|
||||
assert(!block->getTerminator() && "cannot insert the second terminator");
|
||||
block->setTerminator(term);
|
||||
return term;
|
||||
}
|
||||
|
||||
CFGFunction *function;
|
||||
BasicBlock *block = nullptr;
|
||||
BasicBlock::iterator insertPoint;
|
||||
|
|
|
@ -69,9 +69,6 @@ class Instruction : public IROperandOwner {
|
|||
public:
|
||||
enum class Kind {
|
||||
Operation = (int)IROperandOwner::Kind::OperationInst,
|
||||
Branch = (int)IROperandOwner::Kind::BranchInst,
|
||||
CondBranch = (int)IROperandOwner::Kind::CondBranchInst,
|
||||
Return = (int)IROperandOwner::Kind::ReturnInst
|
||||
};
|
||||
|
||||
Kind getKind() const { return (Kind)IROperandOwner::getKind(); }
|
||||
|
@ -444,282 +441,6 @@ private:
|
|||
size_t numTrailingObjects(OverloadToken<unsigned>) const { return numSuccs; }
|
||||
};
|
||||
|
||||
/// Terminator instructions are the last part of a basic block, used to
|
||||
/// represent control flow and returns.
|
||||
class TerminatorInst : public Instruction {
|
||||
public:
|
||||
/// Methods for support type inquiry through isa, cast, and dyn_cast.
|
||||
static bool classof(const Instruction *inst) {
|
||||
return inst->getKind() != Kind::Operation;
|
||||
}
|
||||
|
||||
/// Remove this terminator from its BasicBlock and delete it.
|
||||
void erase();
|
||||
|
||||
/// Return the list of BasicBlockOperand operands of this terminator that
|
||||
/// this terminator holds.
|
||||
MutableArrayRef<BasicBlockOperand> getBasicBlockOperands();
|
||||
|
||||
ArrayRef<BasicBlockOperand> getBasicBlockOperands() const {
|
||||
return const_cast<TerminatorInst *>(this)->getBasicBlockOperands();
|
||||
}
|
||||
|
||||
unsigned getNumSuccessors() const { return getBasicBlockOperands().size(); }
|
||||
|
||||
const BasicBlock *getSuccessor(unsigned i) const {
|
||||
return getBasicBlockOperands()[i].get();
|
||||
}
|
||||
|
||||
BasicBlock *getSuccessor(unsigned i) {
|
||||
return getBasicBlockOperands()[i].get();
|
||||
}
|
||||
|
||||
protected:
|
||||
TerminatorInst(Kind kind, Location location) : Instruction(kind, location) {}
|
||||
~TerminatorInst() {}
|
||||
};
|
||||
|
||||
/// The 'br' instruction is an unconditional from one basic block to another,
|
||||
/// and may pass basic block arguments to the successor.
|
||||
class BranchInst : public TerminatorInst {
|
||||
public:
|
||||
static BranchInst *create(Location location, BasicBlock *dest,
|
||||
ArrayRef<CFGValue *> operands = {}) {
|
||||
return new BranchInst(location, dest, operands);
|
||||
}
|
||||
~BranchInst() {}
|
||||
|
||||
/// Return the block this branch jumps to.
|
||||
BasicBlock *getDest() const { return dest.get(); }
|
||||
void setDest(BasicBlock *block);
|
||||
|
||||
unsigned getNumOperands() const { return operands.size(); }
|
||||
|
||||
ArrayRef<InstOperand> getInstOperands() const { return operands; }
|
||||
MutableArrayRef<InstOperand> getInstOperands() { return operands; }
|
||||
|
||||
/// Add one value to the operand list.
|
||||
void addOperand(CFGValue *value);
|
||||
|
||||
/// Add a list of values to the operand list.
|
||||
void addOperands(ArrayRef<CFGValue *> values);
|
||||
|
||||
/// Erase a specific argument from the arg list.
|
||||
// TODO: void eraseArgument(int Index);
|
||||
|
||||
MutableArrayRef<BasicBlockOperand> getBasicBlockOperands() { return dest; }
|
||||
ArrayRef<BasicBlockOperand> getBasicBlockOperands() const { return dest; }
|
||||
|
||||
/// Methods for support type inquiry through isa, cast, and dyn_cast.
|
||||
static bool classof(const IROperandOwner *ptr) {
|
||||
return ptr->getKind() == IROperandOwner::Kind::BranchInst;
|
||||
}
|
||||
|
||||
private:
|
||||
explicit BranchInst(Location location, BasicBlock *dest,
|
||||
ArrayRef<CFGValue *> operands);
|
||||
BasicBlockOperand dest;
|
||||
std::vector<InstOperand> operands;
|
||||
};
|
||||
|
||||
/// The 'cond_br' instruction is a conditional branch based on a boolean
|
||||
/// condition to one of two possible successors. It may pass arguments to each
|
||||
/// successor.
|
||||
class CondBranchInst : public TerminatorInst {
|
||||
// These are the indices into the dests list.
|
||||
enum { trueIndex = 0, falseIndex = 1 };
|
||||
|
||||
public:
|
||||
static CondBranchInst *create(Location location, CFGValue *condition,
|
||||
BasicBlock *trueDest, BasicBlock *falseDest) {
|
||||
return new CondBranchInst(location, condition, trueDest, falseDest);
|
||||
}
|
||||
~CondBranchInst() {}
|
||||
|
||||
/// Return the i1 condition.
|
||||
CFGValue *getCondition() { return condition; }
|
||||
const CFGValue *getCondition() const { return condition; }
|
||||
|
||||
/// Return the destination if the condition is true.
|
||||
BasicBlock *getTrueDest() const { return dests[trueIndex].get(); }
|
||||
|
||||
/// Return the destination if the condition is false.
|
||||
BasicBlock *getFalseDest() const { return dests[falseIndex].get(); }
|
||||
|
||||
// Support non-const operand iteration.
|
||||
using operand_iterator = OperandIterator<CondBranchInst, CFGValue>;
|
||||
// Support const operand iteration.
|
||||
using const_operand_iterator =
|
||||
OperandIterator<const CondBranchInst, const CFGValue>;
|
||||
|
||||
ArrayRef<InstOperand> getInstOperands() const { return operands; }
|
||||
MutableArrayRef<InstOperand> getInstOperands() { return operands; }
|
||||
|
||||
unsigned getNumOperands() const { return operands.size(); }
|
||||
|
||||
// Accessors for operands to the 'true' destination.
|
||||
CFGValue *getTrueOperand(unsigned idx) {
|
||||
return getTrueInstOperand(idx).get();
|
||||
}
|
||||
const CFGValue *getTrueOperand(unsigned idx) const {
|
||||
return getTrueInstOperand(idx).get();
|
||||
}
|
||||
void setTrueOperand(unsigned idx, CFGValue *value) {
|
||||
return getTrueInstOperand(idx).set(value);
|
||||
}
|
||||
|
||||
operand_iterator true_operand_begin() { return operand_iterator(this, 0); }
|
||||
operand_iterator true_operand_end() {
|
||||
return operand_iterator(this, getNumTrueOperands());
|
||||
}
|
||||
llvm::iterator_range<operand_iterator> getTrueOperands() {
|
||||
return {true_operand_begin(), true_operand_end()};
|
||||
}
|
||||
|
||||
const_operand_iterator true_operand_begin() const {
|
||||
return const_operand_iterator(this, 0);
|
||||
}
|
||||
const_operand_iterator true_operand_end() const {
|
||||
return const_operand_iterator(this, getNumTrueOperands());
|
||||
}
|
||||
llvm::iterator_range<const_operand_iterator> getTrueOperands() const {
|
||||
return {true_operand_begin(), true_operand_end()};
|
||||
}
|
||||
|
||||
ArrayRef<InstOperand> getTrueInstOperands() const {
|
||||
return const_cast<CondBranchInst *>(this)->getTrueInstOperands();
|
||||
}
|
||||
MutableArrayRef<InstOperand> getTrueInstOperands() {
|
||||
return {operands.data(), operands.data() + getNumTrueOperands()};
|
||||
}
|
||||
|
||||
InstOperand &getTrueInstOperand(unsigned idx) { return operands[idx]; }
|
||||
const InstOperand &getTrueInstOperand(unsigned idx) const {
|
||||
return operands[idx];
|
||||
}
|
||||
unsigned getNumTrueOperands() const { return numTrueOperands; }
|
||||
|
||||
/// Add one value to the true operand list.
|
||||
void addTrueOperand(CFGValue *value);
|
||||
|
||||
/// Add a list of values to the operand list.
|
||||
void addTrueOperands(ArrayRef<CFGValue *> values);
|
||||
|
||||
// Accessors for operands to the 'false' destination.
|
||||
CFGValue *getFalseOperand(unsigned idx) {
|
||||
return getFalseInstOperand(idx).get();
|
||||
}
|
||||
const CFGValue *getFalseOperand(unsigned idx) const {
|
||||
return getFalseInstOperand(idx).get();
|
||||
}
|
||||
void setFalseOperand(unsigned idx, CFGValue *value) {
|
||||
return getFalseInstOperand(idx).set(value);
|
||||
}
|
||||
|
||||
operand_iterator false_operand_begin() {
|
||||
return operand_iterator(this, getNumTrueOperands());
|
||||
}
|
||||
operand_iterator false_operand_end() {
|
||||
return operand_iterator(this, getNumOperands());
|
||||
}
|
||||
llvm::iterator_range<operand_iterator> getFalseOperands() {
|
||||
return {false_operand_begin(), false_operand_end()};
|
||||
}
|
||||
|
||||
const_operand_iterator false_operand_begin() const {
|
||||
return const_operand_iterator(this, getNumTrueOperands());
|
||||
}
|
||||
const_operand_iterator false_operand_end() const {
|
||||
return const_operand_iterator(this, getNumOperands());
|
||||
}
|
||||
llvm::iterator_range<const_operand_iterator> getFalseOperands() const {
|
||||
return {false_operand_begin(), false_operand_end()};
|
||||
}
|
||||
|
||||
ArrayRef<InstOperand> getFalseInstOperands() const {
|
||||
return const_cast<CondBranchInst *>(this)->getFalseInstOperands();
|
||||
}
|
||||
MutableArrayRef<InstOperand> getFalseInstOperands() {
|
||||
return {operands.data() + getNumTrueOperands(),
|
||||
operands.data() + getNumOperands()};
|
||||
}
|
||||
|
||||
InstOperand &getFalseInstOperand(unsigned idx) {
|
||||
return operands[idx + getNumTrueOperands()];
|
||||
}
|
||||
const InstOperand &getFalseInstOperand(unsigned idx) const {
|
||||
return operands[idx + getNumTrueOperands()];
|
||||
}
|
||||
unsigned getNumFalseOperands() const {
|
||||
return operands.size() - numTrueOperands;
|
||||
}
|
||||
|
||||
/// Add one value to the false operand list.
|
||||
void addFalseOperand(CFGValue *value);
|
||||
|
||||
/// Add a list of values to the operand list.
|
||||
void addFalseOperands(ArrayRef<CFGValue *> values);
|
||||
|
||||
MutableArrayRef<BasicBlockOperand> getBasicBlockOperands() { return dests; }
|
||||
ArrayRef<BasicBlockOperand> getBasicBlockOperands() const { return dests; }
|
||||
|
||||
/// Methods for support type inquiry through isa, cast, and dyn_cast.
|
||||
static bool classof(const IROperandOwner *ptr) {
|
||||
return ptr->getKind() == IROperandOwner::Kind::CondBranchInst;
|
||||
}
|
||||
|
||||
private:
|
||||
CondBranchInst(Location location, CFGValue *condition, BasicBlock *trueDest,
|
||||
BasicBlock *falseDest);
|
||||
|
||||
CFGValue *condition;
|
||||
BasicBlockOperand dests[2]; // 0 is the true dest, 1 is the false dest.
|
||||
|
||||
// Operand list. The true operands are stored first, followed by the false
|
||||
// operands.
|
||||
std::vector<InstOperand> operands;
|
||||
unsigned numTrueOperands;
|
||||
};
|
||||
|
||||
/// The 'return' instruction represents the end of control flow within the
|
||||
/// current function, and can return zero or more results. The result list is
|
||||
/// required to align with the result list of the containing function's type.
|
||||
class ReturnInst final
|
||||
: public TerminatorInst,
|
||||
private llvm::TrailingObjects<ReturnInst, InstOperand> {
|
||||
public:
|
||||
/// Create a new ReturnInst with the specific fields.
|
||||
static ReturnInst *create(Location location, ArrayRef<CFGValue *> operands);
|
||||
|
||||
unsigned getNumOperands() const { return numOperands; }
|
||||
|
||||
ArrayRef<InstOperand> getInstOperands() const {
|
||||
return {getTrailingObjects<InstOperand>(), numOperands};
|
||||
}
|
||||
MutableArrayRef<InstOperand> getInstOperands() {
|
||||
return {getTrailingObjects<InstOperand>(), numOperands};
|
||||
}
|
||||
|
||||
void destroy();
|
||||
|
||||
/// Methods for support type inquiry through isa, cast, and dyn_cast.
|
||||
static bool classof(const IROperandOwner *ptr) {
|
||||
return ptr->getKind() == IROperandOwner::Kind::ReturnInst;
|
||||
}
|
||||
|
||||
private:
|
||||
// This stuff is used by the TrailingObjects template.
|
||||
friend llvm::TrailingObjects<ReturnInst, InstOperand>;
|
||||
size_t numTrailingObjects(OverloadToken<InstOperand>) const {
|
||||
return numOperands;
|
||||
}
|
||||
|
||||
ReturnInst(Location location, unsigned numOperands);
|
||||
~ReturnInst();
|
||||
|
||||
unsigned numOperands;
|
||||
};
|
||||
|
||||
} // end namespace mlir
|
||||
|
||||
#endif // MLIR_IR_INSTRUCTIONS_H
|
||||
|
|
|
@ -80,9 +80,6 @@ public:
|
|||
ForStmt,
|
||||
IfStmt,
|
||||
OperationInst,
|
||||
BranchInst,
|
||||
CondBranchInst,
|
||||
ReturnInst,
|
||||
|
||||
/// These enums define ranges used for classof implementations.
|
||||
STMT_LAST = IfStmt,
|
||||
|
|
|
@ -49,12 +49,12 @@ bool DominanceInfo::properlyDominates(const Instruction *a,
|
|||
return false;
|
||||
|
||||
// If one is a terminator, then the other dominates it.
|
||||
auto *aOp = dyn_cast<OperationInst>(a);
|
||||
if (!aOp)
|
||||
auto *aOp = cast<OperationInst>(a);
|
||||
if (aOp->isTerminator())
|
||||
return false;
|
||||
|
||||
auto *bOp = dyn_cast<OperationInst>(b);
|
||||
if (!bOp)
|
||||
auto *bOp = cast<OperationInst>(b);
|
||||
if (bOp->isTerminator())
|
||||
return true;
|
||||
|
||||
// Otherwise, do a linear scan to determine whether B comes after A.
|
||||
|
|
|
@ -75,7 +75,7 @@ public:
|
|||
// If the code is properly formed, there will be a terminator. Use its
|
||||
// location.
|
||||
if (auto *termInst = bb.getTerminator())
|
||||
return failure(message, *termInst);
|
||||
return (termInst->emitError(message), true);
|
||||
|
||||
// Worst case, fall back to using the function's location.
|
||||
return failure(message, fn);
|
||||
|
@ -166,14 +166,7 @@ struct CFGFuncVerifier : public Verifier {
|
|||
|
||||
bool verify();
|
||||
bool verifyBlock(const BasicBlock &block);
|
||||
bool verifyTerminator(const TerminatorInst &term);
|
||||
bool verifyInstOperands(const Instruction &inst);
|
||||
|
||||
bool verifyBBArguments(ArrayRef<InstOperand> operands,
|
||||
const BasicBlock *destBB, const TerminatorInst &term);
|
||||
bool verifyReturn(const ReturnInst &inst);
|
||||
bool verifyBranch(const BranchInst &inst);
|
||||
bool verifyCondBranch(const CondBranchInst &inst);
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
|
@ -237,7 +230,10 @@ bool CFGFuncVerifier::verifyBlock(const BasicBlock &block) {
|
|||
if (!block.getTerminator())
|
||||
return failure("basic block with no terminator", block);
|
||||
|
||||
if (verifyTerminator(*block.getTerminator()))
|
||||
// TODO(riverriddle) Remove this when terminators are inside of the block
|
||||
// operation list.
|
||||
auto &term = *block.getTerminator();
|
||||
if (verifyOperation(term) || verifyInstOperands(term))
|
||||
return true;
|
||||
|
||||
for (auto *arg : block.getArguments()) {
|
||||
|
@ -252,101 +248,6 @@ bool CFGFuncVerifier::verifyBlock(const BasicBlock &block) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool CFGFuncVerifier::verifyTerminator(const TerminatorInst &term) {
|
||||
if (term.getFunction() != &fn)
|
||||
return failure("terminator in the wrong function", term);
|
||||
|
||||
// Check that operands are non-nil and structurally ok.
|
||||
for (const auto *operand : term.getOperands()) {
|
||||
if (!operand)
|
||||
return failure("null operand found", term);
|
||||
|
||||
if (operand->getFunction() != &fn)
|
||||
return failure("reference to operand defined in another function", term);
|
||||
}
|
||||
|
||||
// Verify dominance of values.
|
||||
verifyInstOperands(term);
|
||||
|
||||
// Check that successors are in the right function.
|
||||
for (auto *succ : term.getBlock()->getSuccessors()) {
|
||||
if (succ->getFunction() != &fn)
|
||||
return failure("reference to block defined in another function", term);
|
||||
}
|
||||
|
||||
if (auto *ret = dyn_cast<ReturnInst>(&term))
|
||||
return verifyReturn(*ret);
|
||||
|
||||
if (auto *br = dyn_cast<BranchInst>(&term))
|
||||
return verifyBranch(*br);
|
||||
|
||||
if (auto *br = dyn_cast<CondBranchInst>(&term))
|
||||
return verifyCondBranch(*br);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Check a set of basic block arguments against the expected list in in the
|
||||
/// destination basic block.
|
||||
bool CFGFuncVerifier::verifyBBArguments(ArrayRef<InstOperand> operands,
|
||||
const BasicBlock *destBB,
|
||||
const TerminatorInst &term) {
|
||||
if (operands.size() != destBB->getNumArguments())
|
||||
return failure("branch has " + Twine(operands.size()) +
|
||||
" operands, but target block has " +
|
||||
Twine(destBB->getNumArguments()),
|
||||
term);
|
||||
|
||||
for (unsigned i = 0, e = operands.size(); i != e; ++i)
|
||||
if (operands[i].get()->getType() != destBB->getArgument(i)->getType())
|
||||
return failure("type mismatch in bb argument #" + Twine(i), term);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CFGFuncVerifier::verifyReturn(const ReturnInst &inst) {
|
||||
// Verify that the return operands match the results of the function.
|
||||
auto results = fn.getType().getResults();
|
||||
if (inst.getNumOperands() != results.size())
|
||||
return failure("return has " + Twine(inst.getNumOperands()) +
|
||||
" operands, but enclosing function returns " +
|
||||
Twine(results.size()),
|
||||
inst);
|
||||
|
||||
for (unsigned i = 0, e = results.size(); i != e; ++i)
|
||||
if (inst.getOperand(i)->getType() != results[i])
|
||||
return failure("type of return operand " + Twine(i) +
|
||||
" doesn't match function result type",
|
||||
inst);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CFGFuncVerifier::verifyBranch(const BranchInst &inst) {
|
||||
// Verify that the number of operands lines up with the number of BB arguments
|
||||
// in the successor.
|
||||
if (verifyBBArguments(inst.getInstOperands(), inst.getDest(), inst))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CFGFuncVerifier::verifyCondBranch(const CondBranchInst &inst) {
|
||||
// Verify that the number of operands lines up with the number of BB arguments
|
||||
// in the true successor.
|
||||
if (verifyBBArguments(inst.getTrueInstOperands(), inst.getTrueDest(), inst))
|
||||
return true;
|
||||
|
||||
// And the false successor.
|
||||
if (verifyBBArguments(inst.getFalseInstOperands(), inst.getFalseDest(), inst))
|
||||
return true;
|
||||
|
||||
if (inst.getCondition()->getType() != Type::getInteger(1, fn.getContext()))
|
||||
return failure("type of condition is not boolean (i1)", inst);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ML Functions
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -1164,9 +1164,6 @@ public:
|
|||
|
||||
void print(const Instruction *inst);
|
||||
void print(const OperationInst *inst);
|
||||
void print(const ReturnInst *inst);
|
||||
void print(const BranchInst *inst);
|
||||
void print(const CondBranchInst *inst);
|
||||
|
||||
void printSuccessorAndUseList(const Operation *term, unsigned index);
|
||||
|
||||
|
@ -1282,12 +1279,6 @@ void CFGFunctionPrinter::print(const Instruction *inst) {
|
|||
switch (inst->getKind()) {
|
||||
case Instruction::Kind::Operation:
|
||||
return print(cast<OperationInst>(inst));
|
||||
case TerminatorInst::Kind::Branch:
|
||||
return print(cast<BranchInst>(inst));
|
||||
case TerminatorInst::Kind::CondBranch:
|
||||
return print(cast<CondBranchInst>(inst));
|
||||
case TerminatorInst::Kind::Return:
|
||||
return print(cast<ReturnInst>(inst));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1319,40 +1310,6 @@ void CFGFunctionPrinter::printSuccessorAndUseList(const Operation *term,
|
|||
printBranchOperands(term->getSuccessorOperands(index));
|
||||
}
|
||||
|
||||
void CFGFunctionPrinter::print(const BranchInst *inst) {
|
||||
os << "br ";
|
||||
printBBName(inst->getDest());
|
||||
printBranchOperands(inst->getOperands());
|
||||
}
|
||||
|
||||
void CFGFunctionPrinter::print(const CondBranchInst *inst) {
|
||||
os << "cond_br ";
|
||||
printValueID(inst->getCondition());
|
||||
|
||||
os << ", ";
|
||||
printBBName(inst->getTrueDest());
|
||||
printBranchOperands(inst->getTrueOperands());
|
||||
|
||||
os << ", ";
|
||||
printBBName(inst->getFalseDest());
|
||||
printBranchOperands(inst->getFalseOperands());
|
||||
}
|
||||
|
||||
void CFGFunctionPrinter::print(const ReturnInst *inst) {
|
||||
os << "return";
|
||||
|
||||
if (inst->getNumOperands() == 0)
|
||||
return;
|
||||
|
||||
os << ' ';
|
||||
interleaveComma(inst->getOperands(),
|
||||
[&](const CFGValue *operand) { printValueID(operand); });
|
||||
os << " : ";
|
||||
interleaveComma(inst->getOperands(), [&](const CFGValue *operand) {
|
||||
printType(operand->getType());
|
||||
});
|
||||
}
|
||||
|
||||
void ModulePrinter::print(const CFGFunction *fn) {
|
||||
CFGFunctionPrinter(fn, *this).print();
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
// =============================================================================
|
||||
|
||||
#include "mlir/IR/BasicBlock.h"
|
||||
#include "mlir/IR/Builders.h"
|
||||
#include "mlir/IR/BuiltinOps.h"
|
||||
#include "mlir/IR/CFGFunction.h"
|
||||
using namespace mlir;
|
||||
|
||||
|
@ -54,7 +56,7 @@ auto BasicBlock::addArguments(ArrayRef<Type> types)
|
|||
// Terminator management
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
void BasicBlock::setTerminator(TerminatorInst *inst) {
|
||||
void BasicBlock::setTerminator(OperationInst *inst) {
|
||||
assert((!inst || !inst->block) && "terminator already inserted into a block");
|
||||
// If we already had a terminator, abandon it.
|
||||
if (terminator)
|
||||
|
@ -161,9 +163,12 @@ BasicBlock *BasicBlock::splitBasicBlock(iterator splitBefore) {
|
|||
// to the new block.
|
||||
auto branchLoc =
|
||||
splitBefore == end() ? getTerminator()->getLoc() : splitBefore->getLoc();
|
||||
// TODO(riverriddle) Remove this when terminators are a part of the operations
|
||||
// list.
|
||||
auto oldTerm = getTerminator();
|
||||
setTerminator(BranchInst::create(branchLoc, newBB));
|
||||
setTerminator(nullptr);
|
||||
newBB->setTerminator(oldTerm);
|
||||
CFGFuncBuilder(this).create<BranchOp>(branchLoc, newBB);
|
||||
|
||||
// Move all of the operations from the split point to the end of the function
|
||||
// into the new block.
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "mlir/IR/AffineExpr.h"
|
||||
#include "mlir/IR/AffineMap.h"
|
||||
#include "mlir/IR/Attributes.h"
|
||||
#include "mlir/IR/BuiltinOps.h"
|
||||
#include "mlir/IR/IntegerSet.h"
|
||||
#include "mlir/IR/Location.h"
|
||||
#include "mlir/IR/Module.h"
|
||||
|
@ -277,7 +278,15 @@ OperationInst *CFGFuncBuilder::createOperation(const OperationState &state) {
|
|||
auto *op =
|
||||
OperationInst::create(state.location, state.name, operands, state.types,
|
||||
state.attributes, state.successors, context);
|
||||
block->getOperations().insert(insertPoint, op);
|
||||
// TODO(riverriddle) Remove this when the terminators are in basic block
|
||||
// operation lists.
|
||||
if (op->isTerminator()) {
|
||||
// FIXME(b/118738403)
|
||||
assert(!block->getTerminator() && "cannot insert the second terminator");
|
||||
block->setTerminator(op);
|
||||
} else {
|
||||
block->getOperations().insert(insertPoint, op);
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
|
|
|
@ -53,15 +53,6 @@ void Instruction::destroy() {
|
|||
case Kind::Operation:
|
||||
cast<OperationInst>(this)->destroy();
|
||||
break;
|
||||
case Kind::Branch:
|
||||
delete cast<BranchInst>(this);
|
||||
break;
|
||||
case Kind::CondBranch:
|
||||
delete cast<CondBranchInst>(this);
|
||||
break;
|
||||
case Kind::Return:
|
||||
cast<ReturnInst>(this)->destroy();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,12 +70,6 @@ unsigned Instruction::getNumOperands() const {
|
|||
switch (getKind()) {
|
||||
case Kind::Operation:
|
||||
return cast<OperationInst>(this)->getNumOperands();
|
||||
case Kind::Branch:
|
||||
return cast<BranchInst>(this)->getNumOperands();
|
||||
case Kind::CondBranch:
|
||||
return cast<CondBranchInst>(this)->getNumOperands();
|
||||
case Kind::Return:
|
||||
return cast<ReturnInst>(this)->getNumOperands();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,12 +77,6 @@ MutableArrayRef<InstOperand> Instruction::getInstOperands() {
|
|||
switch (getKind()) {
|
||||
case Kind::Operation:
|
||||
return cast<OperationInst>(this)->getInstOperands();
|
||||
case Kind::Branch:
|
||||
return cast<BranchInst>(this)->getInstOperands();
|
||||
case Kind::CondBranch:
|
||||
return cast<CondBranchInst>(this)->getInstOperands();
|
||||
case Kind::Return:
|
||||
return cast<ReturnInst>(this)->getInstOperands();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,10 +87,6 @@ void Instruction::dropAllReferences() {
|
|||
for (auto &op : getInstOperands())
|
||||
op.drop();
|
||||
|
||||
if (auto *term = dyn_cast<TerminatorInst>(this))
|
||||
for (auto &dest : term->getBasicBlockOperands())
|
||||
dest.drop();
|
||||
|
||||
if (OperationInst *opInst = dyn_cast<OperationInst>(this)) {
|
||||
if (opInst->isTerminator())
|
||||
for (auto &dest : opInst->getBasicBlockOperands())
|
||||
|
@ -348,7 +323,14 @@ void llvm::ilist_traits<::mlir::OperationInst>::transferNodesFromList(
|
|||
/// Unlink this instruction from its BasicBlock and delete it.
|
||||
void OperationInst::erase() {
|
||||
assert(getBlock() && "Instruction has no parent");
|
||||
getBlock()->getOperations().erase(this);
|
||||
// TODO(riverriddle) Remove this when terminators are a part of the operations
|
||||
// list.
|
||||
if (isTerminator()) {
|
||||
getBlock()->setTerminator(nullptr);
|
||||
destroy();
|
||||
} else {
|
||||
getBlock()->getOperations().erase(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// Unlink this operation instruction from its current basic block and insert
|
||||
|
@ -356,6 +338,12 @@ void OperationInst::erase() {
|
|||
/// in the same function.
|
||||
void OperationInst::moveBefore(OperationInst *existingInst) {
|
||||
assert(existingInst && "Cannot move before a null instruction");
|
||||
// TODO(riverriddle) Remove this when terminators are a part of the operations
|
||||
// list.
|
||||
if (existingInst->isTerminator()) {
|
||||
return moveBefore(existingInst->getBlock(),
|
||||
existingInst->getBlock()->end());
|
||||
}
|
||||
return moveBefore(existingInst->getBlock(), existingInst->getIterator());
|
||||
}
|
||||
|
||||
|
@ -366,126 +354,3 @@ void OperationInst::moveBefore(BasicBlock *block,
|
|||
block->getOperations().splice(iterator, getBlock()->getOperations(),
|
||||
getIterator());
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// TerminatorInst
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Remove this terminator from its BasicBlock and delete it.
|
||||
void TerminatorInst::erase() {
|
||||
assert(getBlock() && "Instruction has no parent");
|
||||
getBlock()->setTerminator(nullptr);
|
||||
destroy();
|
||||
}
|
||||
|
||||
/// Return the list of destination entries that this terminator branches to.
|
||||
MutableArrayRef<BasicBlockOperand> TerminatorInst::getBasicBlockOperands() {
|
||||
switch (getKind()) {
|
||||
case Kind::Operation:
|
||||
llvm_unreachable("not a terminator");
|
||||
case Kind::Branch:
|
||||
return cast<BranchInst>(this)->getBasicBlockOperands();
|
||||
case Kind::CondBranch:
|
||||
return cast<CondBranchInst>(this)->getBasicBlockOperands();
|
||||
case Kind::Return:
|
||||
// Return has no basic block successors.
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ReturnInst
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Create a new OperationInst with the specific fields.
|
||||
ReturnInst *ReturnInst::create(Location location,
|
||||
ArrayRef<CFGValue *> operands) {
|
||||
auto byteSize = totalSizeToAlloc<InstOperand>(operands.size());
|
||||
void *rawMem = malloc(byteSize);
|
||||
|
||||
// Initialize the ReturnInst part of the instruction.
|
||||
auto inst = ::new (rawMem) ReturnInst(location, operands.size());
|
||||
|
||||
// Initialize the operands and results.
|
||||
auto instOperands = inst->getInstOperands();
|
||||
for (unsigned i = 0, e = operands.size(); i != e; ++i)
|
||||
new (&instOperands[i]) InstOperand(inst, operands[i]);
|
||||
return inst;
|
||||
}
|
||||
|
||||
ReturnInst::ReturnInst(Location location, unsigned numOperands)
|
||||
: TerminatorInst(Kind::Return, location), numOperands(numOperands) {}
|
||||
|
||||
void ReturnInst::destroy() {
|
||||
this->~ReturnInst();
|
||||
free(this);
|
||||
}
|
||||
|
||||
ReturnInst::~ReturnInst() {
|
||||
// Explicitly run the destructors for the operands.
|
||||
for (auto &operand : getInstOperands())
|
||||
operand.~InstOperand();
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// BranchInst
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
BranchInst::BranchInst(Location location, BasicBlock *dest,
|
||||
ArrayRef<CFGValue *> operands)
|
||||
: TerminatorInst(Kind::Branch, location), dest(this, dest) {
|
||||
addOperands(operands);
|
||||
}
|
||||
|
||||
void BranchInst::setDest(BasicBlock *block) { dest.set(block); }
|
||||
|
||||
/// Add one value to the operand list.
|
||||
void BranchInst::addOperand(CFGValue *value) {
|
||||
operands.emplace_back(InstOperand(this, value));
|
||||
}
|
||||
|
||||
/// Add a list of values to the operand list.
|
||||
void BranchInst::addOperands(ArrayRef<CFGValue *> values) {
|
||||
operands.reserve(operands.size() + values.size());
|
||||
for (auto *value : values)
|
||||
addOperand(value);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// CondBranchInst
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
CondBranchInst::CondBranchInst(Location location, CFGValue *condition,
|
||||
BasicBlock *trueDest, BasicBlock *falseDest)
|
||||
: TerminatorInst(Kind::CondBranch, location),
|
||||
condition(condition), dests{{this}, {this}}, numTrueOperands(0) {
|
||||
dests[falseIndex].set(falseDest);
|
||||
dests[trueIndex].set(trueDest);
|
||||
}
|
||||
|
||||
/// Add one value to the true operand list.
|
||||
void CondBranchInst::addTrueOperand(CFGValue *value) {
|
||||
assert(getNumFalseOperands() == 0 &&
|
||||
"Must insert all true operands before false operands!");
|
||||
operands.emplace_back(InstOperand(this, value));
|
||||
++numTrueOperands;
|
||||
}
|
||||
|
||||
/// Add a list of values to the true operand list.
|
||||
void CondBranchInst::addTrueOperands(ArrayRef<CFGValue *> values) {
|
||||
operands.reserve(operands.size() + values.size());
|
||||
for (auto *value : values)
|
||||
addTrueOperand(value);
|
||||
}
|
||||
|
||||
/// Add one value to the false operand list.
|
||||
void CondBranchInst::addFalseOperand(CFGValue *value) {
|
||||
operands.emplace_back(InstOperand(this, value));
|
||||
}
|
||||
|
||||
/// Add a list of values to the false operand list.
|
||||
void CondBranchInst::addFalseOperands(ArrayRef<CFGValue *> values) {
|
||||
operands.reserve(operands.size() + values.size());
|
||||
for (auto *value : values)
|
||||
addFalseOperand(value);
|
||||
}
|
||||
|
|
|
@ -542,7 +542,10 @@ bool OpTrait::impl::verifyIsTerminator(const Operation *op) {
|
|||
} else {
|
||||
const OperationInst *inst = cast<OperationInst>(op);
|
||||
const BasicBlock *block = inst->getBlock();
|
||||
if (!block || &block->back() != inst)
|
||||
// TODO(riverriddle) Check the instruction at the back of the block when
|
||||
// terminators are in the operations list.
|
||||
// if (!block || &block->back() != inst)
|
||||
if (!block || block->getTerminator() != inst)
|
||||
return op->emitOpError(
|
||||
"must be the last instruction in the parent basic block.");
|
||||
}
|
||||
|
|
|
@ -78,9 +78,6 @@ MLIRContext *IROperandOwner::getContext() const {
|
|||
return cast<IfStmt>(this)->getContext();
|
||||
|
||||
case Kind::OperationInst:
|
||||
case Kind::BranchInst:
|
||||
case Kind::CondBranchInst:
|
||||
case Kind::ReturnInst:
|
||||
// If we have an instruction, we can efficiently get this from the function
|
||||
// the instruction is in.
|
||||
auto *fn = cast<Instruction>(this)->getFunction();
|
||||
|
|
|
@ -2133,7 +2133,10 @@ FunctionParser::parseOperation(const CreateOperationFunction &createOpFunc) {
|
|||
// is structurally as we expect. If not, produce an error with a reasonable
|
||||
// source location.
|
||||
if (auto *opInfo = op->getAbstractOperation()) {
|
||||
if (opInfo->verifyInvariants(op))
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
@ -2528,11 +2531,8 @@ private:
|
|||
ParseResult
|
||||
parseOptionalBasicBlockArgList(SmallVectorImpl<BBArgument *> &results,
|
||||
BasicBlock *owner);
|
||||
ParseResult parseBranchBlockAndUseList(BasicBlock *&block,
|
||||
SmallVectorImpl<CFGValue *> &values);
|
||||
|
||||
ParseResult parseBasicBlock();
|
||||
TerminatorInst *parseTerminator();
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
|
@ -2610,6 +2610,15 @@ ParseResult CFGFunctionParser::parseFunctionBody() {
|
|||
return ParseFailure;
|
||||
}
|
||||
|
||||
// Now that the function body has been fully parsed we check the invariants
|
||||
// of any branching terminators.
|
||||
for (auto &block : *function) {
|
||||
auto *term = block.getTerminator();
|
||||
auto *abstractOp = term->getAbstractOperation();
|
||||
if (term->getNumSuccessors() != 0 && abstractOp)
|
||||
abstractOp->verifyInvariants(term);
|
||||
}
|
||||
|
||||
return finalizeFunction(function, braceLoc);
|
||||
}
|
||||
|
||||
|
@ -2657,101 +2666,13 @@ ParseResult CFGFunctionParser::parseBasicBlock() {
|
|||
return ParseFailure;
|
||||
}
|
||||
|
||||
if (!parseTerminator())
|
||||
// Parse the terminator operation.
|
||||
if (parseOperation(createOpFunc))
|
||||
return ParseFailure;
|
||||
|
||||
return ParseSuccess;
|
||||
}
|
||||
|
||||
ParseResult CFGFunctionParser::parseBranchBlockAndUseList(
|
||||
BasicBlock *&block, SmallVectorImpl<CFGValue *> &values) {
|
||||
// Verify branch is identifier and get the matching block.
|
||||
if (!getToken().is(Token::bare_identifier))
|
||||
return emitError("expected basic block name");
|
||||
block = getBlockNamed(getTokenSpelling(), getToken().getLoc());
|
||||
consumeToken();
|
||||
|
||||
// Handle optional arguments.
|
||||
if (consumeIf(Token::l_paren) &&
|
||||
(parseOptionalSSAUseAndTypeList(values) ||
|
||||
parseToken(Token::r_paren, "expected ')' to close argument list"))) {
|
||||
return ParseFailure;
|
||||
}
|
||||
|
||||
return ParseSuccess;
|
||||
}
|
||||
|
||||
/// Parse the terminator instruction for a basic block.
|
||||
///
|
||||
/// terminator-stmt ::= `br` bb-id branch-use-list?
|
||||
/// branch-use-list ::= `(` ssa-use-list ':' type-list-no-parens `)`
|
||||
/// terminator-stmt ::=
|
||||
/// `cond_br` ssa-use `,` bb-id branch-use-list? `,` bb-id
|
||||
/// branch-use-list?
|
||||
/// terminator-stmt ::= `return` ssa-use-and-type-list?
|
||||
///
|
||||
TerminatorInst *CFGFunctionParser::parseTerminator() {
|
||||
auto loc = getToken().getLoc();
|
||||
|
||||
switch (getToken().getKind()) {
|
||||
default:
|
||||
return (emitError("expected terminator at end of basic block"), nullptr);
|
||||
|
||||
case Token::kw_return: {
|
||||
consumeToken(Token::kw_return);
|
||||
|
||||
// Parse any operands.
|
||||
SmallVector<CFGValue *, 8> operands;
|
||||
if (parseOptionalSSAUseAndTypeList(operands))
|
||||
return nullptr;
|
||||
return builder.createReturn(getEncodedSourceLocation(loc), operands);
|
||||
}
|
||||
|
||||
case Token::kw_br: {
|
||||
consumeToken(Token::kw_br);
|
||||
BasicBlock *destBB;
|
||||
SmallVector<CFGValue *, 4> values;
|
||||
if (parseBranchBlockAndUseList(destBB, values))
|
||||
return nullptr;
|
||||
auto branch = builder.createBranch(getEncodedSourceLocation(loc), destBB);
|
||||
branch->addOperands(values);
|
||||
return branch;
|
||||
}
|
||||
|
||||
case Token::kw_cond_br: {
|
||||
consumeToken(Token::kw_cond_br);
|
||||
SSAUseInfo ssaUse;
|
||||
if (parseSSAUse(ssaUse))
|
||||
return nullptr;
|
||||
auto *cond = resolveSSAUse(ssaUse, builder.getIntegerType(1));
|
||||
if (!cond)
|
||||
return (emitError("expected type was boolean (i1)"), nullptr);
|
||||
if (parseToken(Token::comma, "expected ',' in conditional branch"))
|
||||
return nullptr;
|
||||
|
||||
BasicBlock *trueBlock;
|
||||
SmallVector<CFGValue *, 4> trueOperands;
|
||||
if (parseBranchBlockAndUseList(trueBlock, trueOperands))
|
||||
return nullptr;
|
||||
|
||||
if (parseToken(Token::comma, "expected ',' in conditional branch"))
|
||||
return nullptr;
|
||||
|
||||
BasicBlock *falseBlock;
|
||||
SmallVector<CFGValue *, 4> falseOperands;
|
||||
if (parseBranchBlockAndUseList(falseBlock, falseOperands))
|
||||
return nullptr;
|
||||
|
||||
auto branch =
|
||||
builder.createCondBranch(getEncodedSourceLocation(loc),
|
||||
cast<CFGValue>(cond), trueBlock, falseBlock);
|
||||
branch->addTrueOperands(trueOperands);
|
||||
branch->addFalseOperands(falseOperands);
|
||||
return branch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ML Functions
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -62,19 +62,13 @@ private:
|
|||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
// Return a vector of OperationStmt's arguments as the CFGValues or SSAValues
|
||||
// depending on the template argument. For each statement operands, represented
|
||||
// as MLValue, lookup its CFGValue conterpart in the valueRemapping table.
|
||||
// The return type parameterization is necessary because some instructions
|
||||
// accept vectors of SSAValues while others accept vectors of CFGValues.
|
||||
template <typename SSAValueTy>
|
||||
static llvm::SmallVector<SSAValueTy *, 4>
|
||||
// Return a vector of OperationStmt's arguments as SSAValues. For each
|
||||
// statement operands, represented as MLValue, lookup its CFGValue conterpart in
|
||||
// the valueRemapping table.
|
||||
static llvm::SmallVector<SSAValue *, 4>
|
||||
operandsAs(OperationStmt *opStmt,
|
||||
const llvm::DenseMap<const MLValue *, CFGValue *> &valueRemapping) {
|
||||
static_assert(std::is_same<SSAValueTy, SSAValue>::value ||
|
||||
std::is_same<SSAValueTy, CFGValue>::value,
|
||||
"can only cast statement operands to CFGValue or SSAValue");
|
||||
llvm::SmallVector<SSAValueTy *, 4> operands;
|
||||
llvm::SmallVector<SSAValue *, 4> operands;
|
||||
for (const MLValue *operand : opStmt->getOperands()) {
|
||||
assert(valueRemapping.count(operand) != 0 && "operand is not defined");
|
||||
operands.push_back(valueRemapping.lookup(operand));
|
||||
|
@ -89,20 +83,10 @@ operandsAs(OperationStmt *opStmt,
|
|||
// mapping MLValue->CFGValue as the conversion is performed. The operation
|
||||
// instruction is appended to current block (end of SESE region).
|
||||
void FunctionConverter::visitOperationStmt(OperationStmt *opStmt) {
|
||||
// Handle returns separately, they are transformed into a specially-typed
|
||||
// return instruction.
|
||||
// TODO(zinenko): after terminators and operations are merged, remove this
|
||||
// special case and de-template operandsAs.
|
||||
if (opStmt->getName().getStringRef() == ReturnOp::getOperationName()) {
|
||||
builder.createReturn(opStmt->getLoc(),
|
||||
operandsAs<CFGValue>(opStmt, valueRemapping));
|
||||
return;
|
||||
}
|
||||
|
||||
// Set up basic operation state (context, name, operands).
|
||||
OperationState state(cfgFunc->getContext(), opStmt->getLoc(),
|
||||
opStmt->getName());
|
||||
state.addOperands(operandsAs<SSAValue>(opStmt, valueRemapping));
|
||||
state.addOperands(operandsAs(opStmt, valueRemapping));
|
||||
|
||||
// Set up operation return types. The corresponding SSAValues will become
|
||||
// available after the operation is created.
|
||||
|
@ -206,7 +190,7 @@ void FunctionConverter::visitForStmt(ForStmt *forStmt) {
|
|||
|
||||
// At the loop insertion location, branch immediately to the loop init block.
|
||||
builder.setInsertionPoint(loopInsertionPoint);
|
||||
builder.createBranch(builder.getUnknownLoc(), loopInitBlock);
|
||||
builder.create<BranchOp>(builder.getUnknownLoc(), loopInitBlock);
|
||||
|
||||
// The loop condition block has an argument for loop induction variable.
|
||||
// Create it upfront and make the loop induction variable -> basic block
|
||||
|
@ -230,8 +214,8 @@ void FunctionConverter::visitForStmt(ForStmt *forStmt) {
|
|||
CFGValue *step = getConstantIndexValue(forStmt->getStep());
|
||||
auto stepOp = builder.create<AddIOp>(forStmt->getLoc(), iv, step);
|
||||
CFGValue *nextIvValue = cast<CFGValue>(stepOp->getResult());
|
||||
builder.createBranch(builder.getUnknownLoc(), loopConditionBlock,
|
||||
{nextIvValue});
|
||||
builder.create<BranchOp>(builder.getUnknownLoc(), loopConditionBlock,
|
||||
nextIvValue);
|
||||
|
||||
// Create post-loop block here so that it appears after all loop body blocks.
|
||||
BasicBlock *postLoopBlock = builder.createBlock();
|
||||
|
@ -243,15 +227,15 @@ void FunctionConverter::visitForStmt(ForStmt *forStmt) {
|
|||
getConstantIndexValue(forStmt->getConstantLowerBound());
|
||||
CFGValue *upperBound =
|
||||
getConstantIndexValue(forStmt->getConstantUpperBound());
|
||||
builder.createBranch(builder.getUnknownLoc(), loopConditionBlock,
|
||||
{lowerBound});
|
||||
builder.create<BranchOp>(builder.getUnknownLoc(), loopConditionBlock,
|
||||
lowerBound);
|
||||
|
||||
builder.setInsertionPoint(loopConditionBlock);
|
||||
auto comparisonOp = builder.create<CmpIOp>(
|
||||
forStmt->getLoc(), CmpIPredicate::SLT, iv, upperBound);
|
||||
auto comparisonResult = cast<CFGValue>(comparisonOp->getResult());
|
||||
builder.createCondBranch(builder.getUnknownLoc(), comparisonResult,
|
||||
loopBodyFirstBlock, postLoopBlock);
|
||||
builder.create<CondBranchOp>(builder.getUnknownLoc(), comparisonResult,
|
||||
loopBodyFirstBlock, postLoopBlock);
|
||||
|
||||
// Finally, make sure building can continue by setting the post-loop block
|
||||
// (end of loop SESE region) as the insertion point.
|
||||
|
|
|
@ -338,7 +338,7 @@ mlfunc @malformed_type(%a : intt) { // expected-error {{expected type}}
|
|||
|
||||
cfgfunc @resulterror() -> i32 {
|
||||
bb42:
|
||||
return // expected-error {{return has 0 operands, but enclosing function returns 1}}
|
||||
return // expected-error {{'return' op has 0 operands, but enclosing function returns 1}}
|
||||
}
|
||||
|
||||
// -----
|
||||
|
@ -387,7 +387,7 @@ cfgfunc @condbr_notbool() {
|
|||
bb0:
|
||||
%a = "foo"() : () -> i32 // expected-error {{prior use here}}
|
||||
cond_br %a, bb0, bb0 // expected-error {{use of value '%a' expects different type than prior uses}}
|
||||
// expected-error@-1 {{expected type was boolean (i1)}}
|
||||
// expected-error@-1 {{expected condition type was boolean (i1)}}
|
||||
}
|
||||
|
||||
// -----
|
||||
|
|
Loading…
Reference in New Issue