2019-03-01 13:48:24 -08:00
|
|
|
//===- Ops.cpp - Standard MLIR Operations ---------------------------------===//
|
2018-07-05 09:12:11 -07:00
|
|
|
//
|
|
|
|
// Copyright 2019 The MLIR Authors.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
// =============================================================================
|
|
|
|
|
2019-03-01 13:48:24 -08:00
|
|
|
#include "mlir/StandardOps/Ops.h"
|
2019-04-27 20:55:38 -07:00
|
|
|
|
2018-09-24 10:23:02 -07:00
|
|
|
#include "mlir/IR/AffineExpr.h"
|
2018-07-24 10:13:31 -07:00
|
|
|
#include "mlir/IR/AffineMap.h"
|
2018-07-25 11:15:20 -07:00
|
|
|
#include "mlir/IR/Builders.h"
|
2018-10-29 10:22:49 -07:00
|
|
|
#include "mlir/IR/Matchers.h"
|
2019-05-22 13:41:23 -07:00
|
|
|
#include "mlir/IR/Module.h"
|
2018-07-24 16:07:22 -07:00
|
|
|
#include "mlir/IR/OpImplementation.h"
|
2018-10-25 16:44:04 -07:00
|
|
|
#include "mlir/IR/PatternMatch.h"
|
2019-01-03 14:29:52 -08:00
|
|
|
#include "mlir/IR/StandardTypes.h"
|
2018-12-27 14:35:10 -08:00
|
|
|
#include "mlir/IR/Value.h"
|
2018-10-03 10:07:54 -07:00
|
|
|
#include "mlir/Support/MathExtras.h"
|
2018-08-09 12:28:58 -07:00
|
|
|
#include "mlir/Support/STLExtras.h"
|
2018-11-08 04:02:00 -08:00
|
|
|
#include "llvm/ADT/StringSwitch.h"
|
2019-04-27 20:55:38 -07:00
|
|
|
#include "llvm/Support/FormatVariadic.h"
|
2018-07-05 09:12:11 -07:00
|
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
using namespace mlir;
|
|
|
|
|
2018-10-21 19:49:31 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// StandardOpsDialect
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2019-03-02 18:03:03 -08:00
|
|
|
/// A custom binary operation printer that omits the "std." prefix from the
|
|
|
|
/// operation names.
|
2019-05-13 11:56:21 -07:00
|
|
|
static void printStandardBinaryOp(Operation *op, OpAsmPrinter *p) {
|
2019-03-02 18:03:03 -08:00
|
|
|
assert(op->getNumOperands() == 2 && "binary op should have two operands");
|
|
|
|
assert(op->getNumResults() == 1 && "binary op should have one result");
|
|
|
|
|
|
|
|
// If not all the operand and result types are the same, just use the
|
|
|
|
// generic assembly form to avoid omitting information in printing.
|
|
|
|
auto resultType = op->getResult(0)->getType();
|
|
|
|
if (op->getOperand(0)->getType() != resultType ||
|
|
|
|
op->getOperand(1)->getType() != resultType) {
|
|
|
|
p->printGenericOp(op);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
*p << op->getName().getStringRef().drop_front(strlen("std.")) << ' '
|
|
|
|
<< *op->getOperand(0) << ", " << *op->getOperand(1);
|
|
|
|
p->printOptionalAttrDict(op->getAttrs());
|
|
|
|
|
|
|
|
// Now we can output only one type for all operands and the result.
|
|
|
|
*p << " : " << op->getResult(0)->getType();
|
|
|
|
}
|
|
|
|
|
2019-05-13 11:56:21 -07:00
|
|
|
/// A custom cast operation printer that omits the "std." prefix from the
|
|
|
|
/// operation names.
|
|
|
|
static void printStandardCastOp(Operation *op, OpAsmPrinter *p) {
|
|
|
|
*p << op->getName().getStringRef().drop_front(strlen("std.")) << ' '
|
|
|
|
<< *op->getOperand(0) << " : " << op->getOperand(0)->getType() << " to "
|
|
|
|
<< op->getResult(0)->getType();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A custom cast operation verifier.
|
|
|
|
template <typename T> static LogicalResult verifyCastOp(T op) {
|
|
|
|
auto opType = op.getOperand()->getType();
|
|
|
|
auto resType = op.getType();
|
|
|
|
if (!T::areCastCompatible(opType, resType))
|
|
|
|
return op.emitError("operand type ") << opType << " and result type "
|
|
|
|
<< resType << " are cast incompatible";
|
|
|
|
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2018-10-21 19:49:31 -07:00
|
|
|
StandardOpsDialect::StandardOpsDialect(MLIRContext *context)
|
2019-03-29 22:30:54 -07:00
|
|
|
: Dialect(/*name=*/"std", context) {
|
2019-05-24 18:01:20 -07:00
|
|
|
addOperations<CondBranchOp, DmaStartOp, DmaWaitOp, LoadOp, StoreOp,
|
2019-01-04 01:34:16 -08:00
|
|
|
#define GET_OP_LIST
|
2019-03-20 17:25:34 -07:00
|
|
|
#include "mlir/StandardOps/Ops.cpp.inc"
|
2019-01-04 01:34:16 -08:00
|
|
|
>();
|
2018-10-21 19:49:31 -07:00
|
|
|
}
|
|
|
|
|
2019-03-28 08:24:38 -07:00
|
|
|
void mlir::printDimAndSymbolList(Operation::operand_iterator begin,
|
|
|
|
Operation::operand_iterator end,
|
2019-03-01 16:58:00 -08:00
|
|
|
unsigned numDims, OpAsmPrinter *p) {
|
|
|
|
*p << '(';
|
|
|
|
p->printOperands(begin, begin + numDims);
|
|
|
|
*p << ')';
|
|
|
|
|
|
|
|
if (begin + numDims != end) {
|
|
|
|
*p << '[';
|
|
|
|
p->printOperands(begin + numDims, end);
|
|
|
|
*p << ']';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parses dimension and symbol list, and sets 'numDims' to the number of
|
|
|
|
// dimension operands parsed.
|
|
|
|
// Returns 'false' on success and 'true' on error.
|
2019-05-06 22:01:31 -07:00
|
|
|
ParseResult mlir::parseDimAndSymbolList(OpAsmParser *parser,
|
|
|
|
SmallVector<Value *, 4> &operands,
|
|
|
|
unsigned &numDims) {
|
2019-03-01 16:58:00 -08:00
|
|
|
SmallVector<OpAsmParser::OperandType, 8> opInfos;
|
|
|
|
if (parser->parseOperandList(opInfos, -1, OpAsmParser::Delimiter::Paren))
|
2019-05-06 22:01:31 -07:00
|
|
|
return failure();
|
2019-03-01 16:58:00 -08:00
|
|
|
// Store number of dimensions for validation by caller.
|
|
|
|
numDims = opInfos.size();
|
|
|
|
|
|
|
|
// Parse the optional symbol operands.
|
|
|
|
auto affineIntTy = parser->getBuilder().getIndexType();
|
|
|
|
if (parser->parseOperandList(opInfos, -1,
|
|
|
|
OpAsmParser::Delimiter::OptionalSquare) ||
|
|
|
|
parser->resolveOperands(opInfos, affineIntTy, operands))
|
2019-05-06 22:01:31 -07:00
|
|
|
return failure();
|
|
|
|
return success();
|
2019-03-01 16:58:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Matches a ConstantIndexOp.
|
|
|
|
/// TODO: This should probably just be a general matcher that uses m_Constant
|
|
|
|
/// and checks the operation for an index type.
|
|
|
|
static detail::op_matcher<ConstantIndexOp> m_ConstantIndex() {
|
|
|
|
return detail::op_matcher<ConstantIndexOp>();
|
|
|
|
}
|
|
|
|
|
2018-10-25 16:44:04 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Common canonicalization pattern support logic
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
/// This is a common class used for patterns of the form
|
|
|
|
/// "someop(memrefcast) -> someop". It folds the source of any memref_cast
|
|
|
|
/// into the root operation directly.
|
2018-11-28 15:09:39 -08:00
|
|
|
struct MemRefCastFolder : public RewritePattern {
|
2018-10-25 16:44:04 -07:00
|
|
|
/// The rootOpName is the name of the root operation to match against.
|
|
|
|
MemRefCastFolder(StringRef rootOpName, MLIRContext *context)
|
2018-11-28 15:09:39 -08:00
|
|
|
: RewritePattern(rootOpName, 1, context) {}
|
2018-10-25 16:44:04 -07:00
|
|
|
|
2019-03-28 08:24:38 -07:00
|
|
|
PatternMatchResult match(Operation *op) const override {
|
2018-10-25 16:44:04 -07:00
|
|
|
for (auto *operand : op->getOperands())
|
2018-10-30 10:57:50 -07:00
|
|
|
if (matchPattern(operand, m_Op<MemRefCastOp>()))
|
2018-10-29 10:22:49 -07:00
|
|
|
return matchSuccess();
|
2018-10-25 16:44:04 -07:00
|
|
|
|
|
|
|
return matchFailure();
|
|
|
|
}
|
|
|
|
|
2019-03-28 08:24:38 -07:00
|
|
|
void rewrite(Operation *op, PatternRewriter &rewriter) const override {
|
2018-10-25 16:44:04 -07:00
|
|
|
for (unsigned i = 0, e = op->getNumOperands(); i != e; ++i)
|
2019-03-26 17:05:09 -07:00
|
|
|
if (auto *memref = op->getOperand(i)->getDefiningOp())
|
2019-05-11 15:56:50 -07:00
|
|
|
if (auto cast = dyn_cast<MemRefCastOp>(memref))
|
2019-03-25 13:02:06 -07:00
|
|
|
op->setOperand(i, cast.getOperand());
|
2018-10-25 16:44:04 -07:00
|
|
|
rewriter.updatedRootInPlace(op);
|
|
|
|
}
|
|
|
|
};
|
2019-01-11 09:12:11 -08:00
|
|
|
|
2019-01-15 14:30:10 -08:00
|
|
|
/// Performs const folding `calculate` with element-wise behavior on the two
|
|
|
|
/// attributes in `operands` and returns the result if possible.
|
2019-01-11 09:12:11 -08:00
|
|
|
template <class AttrElementT,
|
|
|
|
class ElementValueT = typename AttrElementT::ValueType,
|
|
|
|
class CalculationT =
|
|
|
|
std::function<ElementValueT(ElementValueT, ElementValueT)>>
|
|
|
|
Attribute constFoldBinaryOp(ArrayRef<Attribute> operands,
|
2019-01-15 14:30:10 -08:00
|
|
|
const CalculationT &calculate) {
|
2019-01-11 09:12:11 -08:00
|
|
|
assert(operands.size() == 2 && "binary op takes two operands");
|
|
|
|
|
|
|
|
if (auto lhs = operands[0].dyn_cast_or_null<AttrElementT>()) {
|
|
|
|
auto rhs = operands[1].dyn_cast_or_null<AttrElementT>();
|
|
|
|
if (!rhs || lhs.getType() != rhs.getType())
|
|
|
|
return {};
|
|
|
|
|
|
|
|
return AttrElementT::get(lhs.getType(),
|
2019-01-15 14:30:10 -08:00
|
|
|
calculate(lhs.getValue(), rhs.getValue()));
|
2019-01-11 09:12:11 -08:00
|
|
|
} else if (auto lhs = operands[0].dyn_cast_or_null<SplatElementsAttr>()) {
|
|
|
|
auto rhs = operands[1].dyn_cast_or_null<SplatElementsAttr>();
|
|
|
|
if (!rhs || lhs.getType() != rhs.getType())
|
|
|
|
return {};
|
|
|
|
|
|
|
|
auto elementResult = constFoldBinaryOp<AttrElementT>(
|
2019-01-15 14:30:10 -08:00
|
|
|
{lhs.getValue(), rhs.getValue()}, calculate);
|
2019-01-11 09:12:11 -08:00
|
|
|
if (!elementResult)
|
|
|
|
return {};
|
|
|
|
|
|
|
|
return SplatElementsAttr::get(lhs.getType(), elementResult);
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
2018-10-25 16:44:04 -07:00
|
|
|
} // end anonymous namespace.
|
|
|
|
|
2018-08-09 12:28:58 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// AddFOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2019-05-16 12:51:45 -07:00
|
|
|
OpFoldResult AddFOp::fold(ArrayRef<Attribute> operands) {
|
2019-01-11 09:12:11 -08:00
|
|
|
return constFoldBinaryOp<FloatAttr>(
|
|
|
|
operands, [](APFloat a, APFloat b) { return a + b; });
|
2018-09-19 21:35:11 -07:00
|
|
|
}
|
|
|
|
|
2018-10-03 09:43:13 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// AddIOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2019-05-16 12:51:45 -07:00
|
|
|
OpFoldResult AddIOp::fold(ArrayRef<Attribute> operands) {
|
2019-01-24 12:34:00 -08:00
|
|
|
/// addi(x, 0) -> x
|
2019-05-16 12:51:45 -07:00
|
|
|
if (matchPattern(rhs(), m_Zero()))
|
|
|
|
return lhs();
|
2018-10-25 16:44:04 -07:00
|
|
|
|
2019-05-16 12:51:45 -07:00
|
|
|
return constFoldBinaryOp<IntegerAttr>(operands,
|
|
|
|
[](APInt a, APInt b) { return a + b; });
|
2018-10-25 16:44:04 -07:00
|
|
|
}
|
|
|
|
|
2018-08-09 12:28:58 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// AllocOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2019-05-14 08:27:32 -07:00
|
|
|
static void print(OpAsmPrinter *p, AllocOp op) {
|
2018-07-30 13:08:05 -07:00
|
|
|
*p << "alloc";
|
2019-05-08 22:38:01 -07:00
|
|
|
|
2018-07-30 13:08:05 -07:00
|
|
|
// Print dynamic dimension operands.
|
2019-05-08 22:38:01 -07:00
|
|
|
MemRefType type = op.getType();
|
|
|
|
printDimAndSymbolList(op.operand_begin(), op.operand_end(),
|
2018-10-30 14:59:22 -07:00
|
|
|
type.getNumDynamicDims(), p);
|
2019-05-08 22:38:01 -07:00
|
|
|
p->printOptionalAttrDict(op.getAttrs(), /*elidedAttrs=*/{"map"});
|
2018-10-30 14:59:22 -07:00
|
|
|
*p << " : " << type;
|
2018-07-30 13:08:05 -07:00
|
|
|
}
|
|
|
|
|
2019-05-08 22:38:01 -07:00
|
|
|
static ParseResult parseAllocOp(OpAsmParser *parser, OperationState *result) {
|
2018-10-30 14:59:22 -07:00
|
|
|
MemRefType type;
|
2018-07-30 13:08:05 -07:00
|
|
|
|
2018-07-31 16:21:36 -07:00
|
|
|
// Parse the dimension operands and optional symbol operands, followed by a
|
|
|
|
// memref type.
|
2018-07-30 13:08:05 -07:00
|
|
|
unsigned numDimOperands;
|
2018-08-07 09:12:35 -07:00
|
|
|
if (parseDimAndSymbolList(parser, result->operands, numDimOperands) ||
|
|
|
|
parser->parseOptionalAttributeDict(result->attributes) ||
|
|
|
|
parser->parseColonType(type))
|
2019-05-06 22:01:31 -07:00
|
|
|
return failure();
|
2018-07-30 13:08:05 -07:00
|
|
|
|
|
|
|
// Check numDynamicDims against number of question marks in memref type.
|
2018-09-20 09:39:55 -07:00
|
|
|
// Note: this check remains here (instead of in verify()), because the
|
|
|
|
// partition between dim operands and symbol operands is lost after parsing.
|
|
|
|
// Verification still checks that the total number of operands matches
|
|
|
|
// the number of symbols in the affine map, plus the number of dynamic
|
|
|
|
// dimensions in the memref.
|
2019-05-08 22:38:01 -07:00
|
|
|
if (numDimOperands != type.getNumDynamicDims())
|
|
|
|
return parser->emitError(parser->getNameLoc())
|
|
|
|
<< "dimension operand count does not equal memref dynamic dimension "
|
|
|
|
"count";
|
2018-08-07 09:12:35 -07:00
|
|
|
result->types.push_back(type);
|
2019-05-06 22:01:31 -07:00
|
|
|
return success();
|
2018-07-30 13:08:05 -07:00
|
|
|
}
|
|
|
|
|
2019-05-08 22:38:01 -07:00
|
|
|
static LogicalResult verify(AllocOp op) {
|
|
|
|
auto memRefType = op.getResult()->getType().dyn_cast<MemRefType>();
|
2018-09-20 09:39:55 -07:00
|
|
|
if (!memRefType)
|
2019-05-08 22:38:01 -07:00
|
|
|
return op.emitOpError("result must be a memref");
|
2018-09-20 09:39:55 -07:00
|
|
|
|
|
|
|
unsigned numSymbols = 0;
|
2018-10-30 14:59:22 -07:00
|
|
|
if (!memRefType.getAffineMaps().empty()) {
|
|
|
|
AffineMap affineMap = memRefType.getAffineMaps()[0];
|
2018-09-20 09:39:55 -07:00
|
|
|
// Store number of symbols used in affine map (used in subsequent check).
|
2018-10-09 16:39:24 -07:00
|
|
|
numSymbols = affineMap.getNumSymbols();
|
2018-10-26 06:15:38 -07:00
|
|
|
// TODO(zinenko): this check does not belong to AllocOp, or any other op but
|
|
|
|
// to the type system itself. It has been partially hoisted to Parser but
|
|
|
|
// remains here in case an AllocOp gets constructed programmatically.
|
|
|
|
// Remove when we can emit errors directly from *Type::get(...) functions.
|
|
|
|
//
|
2018-09-20 09:39:55 -07:00
|
|
|
// Verify that the layout affine map matches the rank of the memref.
|
2018-10-30 14:59:22 -07:00
|
|
|
if (affineMap.getNumDims() != memRefType.getRank())
|
2019-05-08 22:38:01 -07:00
|
|
|
return op.emitOpError(
|
|
|
|
"affine map dimension count must equal memref rank");
|
2018-09-20 09:39:55 -07:00
|
|
|
}
|
2018-10-30 14:59:22 -07:00
|
|
|
unsigned numDynamicDims = memRefType.getNumDynamicDims();
|
2018-09-20 09:39:55 -07:00
|
|
|
// Check that the total number of operands matches the number of symbols in
|
|
|
|
// the affine map, plus the number of dynamic dimensions specified in the
|
|
|
|
// memref type.
|
2019-05-08 22:38:01 -07:00
|
|
|
if (op.getOperation()->getNumOperands() != numDynamicDims + numSymbols)
|
|
|
|
return op.emitOpError(
|
2018-09-20 09:39:55 -07:00
|
|
|
"operand count does not equal dimension plus symbol operand count");
|
2019-04-02 13:09:34 -07:00
|
|
|
|
2018-10-06 17:21:53 -07:00
|
|
|
// Verify that all operands are of type Index.
|
2019-05-24 13:28:55 -07:00
|
|
|
for (auto operandType : op.getOperandTypes())
|
|
|
|
if (!operandType.isIndex())
|
2019-05-08 22:38:01 -07:00
|
|
|
return op.emitOpError("requires operands to be of type Index");
|
2019-04-02 13:09:34 -07:00
|
|
|
return success();
|
2018-07-30 13:08:05 -07:00
|
|
|
}
|
|
|
|
|
2018-10-25 16:44:04 -07:00
|
|
|
namespace {
|
2019-03-28 08:24:38 -07:00
|
|
|
/// Fold constant dimensions into an alloc operation.
|
2018-11-28 15:09:39 -08:00
|
|
|
struct SimplifyAllocConst : public RewritePattern {
|
2018-10-25 16:44:04 -07:00
|
|
|
SimplifyAllocConst(MLIRContext *context)
|
2018-11-28 15:09:39 -08:00
|
|
|
: RewritePattern(AllocOp::getOperationName(), 1, context) {}
|
2018-10-25 16:44:04 -07:00
|
|
|
|
2019-03-28 08:24:38 -07:00
|
|
|
PatternMatchResult match(Operation *op) const override {
|
2019-05-11 17:57:32 -07:00
|
|
|
auto alloc = cast<AllocOp>(op);
|
2018-10-25 16:44:04 -07:00
|
|
|
|
|
|
|
// Check to see if any dimensions operands are constants. If so, we can
|
|
|
|
// substitute and drop them.
|
2019-03-25 13:02:06 -07:00
|
|
|
for (auto *operand : alloc.getOperands())
|
2018-10-30 10:57:50 -07:00
|
|
|
if (matchPattern(operand, m_ConstantIndex()))
|
2018-10-29 10:22:49 -07:00
|
|
|
return matchSuccess();
|
2018-10-25 16:44:04 -07:00
|
|
|
return matchFailure();
|
|
|
|
}
|
|
|
|
|
2019-03-28 08:24:38 -07:00
|
|
|
void rewrite(Operation *op, PatternRewriter &rewriter) const override {
|
2019-05-11 17:57:32 -07:00
|
|
|
auto allocOp = cast<AllocOp>(op);
|
2019-03-25 13:02:06 -07:00
|
|
|
auto memrefType = allocOp.getType();
|
2018-10-25 16:44:04 -07:00
|
|
|
|
|
|
|
// Ok, we have one or more constant operands. Collect the non-constant ones
|
|
|
|
// and keep track of the resultant memref type to build.
|
2019-01-23 14:39:45 -08:00
|
|
|
SmallVector<int64_t, 4> newShapeConstants;
|
2018-10-30 14:59:22 -07:00
|
|
|
newShapeConstants.reserve(memrefType.getRank());
|
2018-12-27 14:35:10 -08:00
|
|
|
SmallVector<Value *, 4> newOperands;
|
|
|
|
SmallVector<Value *, 4> droppedOperands;
|
2018-10-25 16:44:04 -07:00
|
|
|
|
|
|
|
unsigned dynamicDimPos = 0;
|
2018-10-30 14:59:22 -07:00
|
|
|
for (unsigned dim = 0, e = memrefType.getRank(); dim < e; ++dim) {
|
2019-01-23 14:39:45 -08:00
|
|
|
int64_t dimSize = memrefType.getDimSize(dim);
|
2018-10-25 16:44:04 -07:00
|
|
|
// If this is already static dimension, keep it.
|
|
|
|
if (dimSize != -1) {
|
|
|
|
newShapeConstants.push_back(dimSize);
|
|
|
|
continue;
|
|
|
|
}
|
2019-03-26 17:05:09 -07:00
|
|
|
auto *defOp = allocOp.getOperand(dynamicDimPos)->getDefiningOp();
|
2019-04-05 12:24:03 -07:00
|
|
|
if (auto constantIndexOp = dyn_cast_or_null<ConstantIndexOp>(defOp)) {
|
2018-10-25 16:44:04 -07:00
|
|
|
// Dynamic shape dimension will be folded.
|
2019-03-24 19:53:05 -07:00
|
|
|
newShapeConstants.push_back(constantIndexOp.getValue());
|
2018-10-25 16:44:04 -07:00
|
|
|
// Record to check for zero uses later below.
|
|
|
|
droppedOperands.push_back(constantIndexOp);
|
|
|
|
} else {
|
|
|
|
// Dynamic shape dimension not folded; copy operand from old memref.
|
|
|
|
newShapeConstants.push_back(-1);
|
2019-03-25 13:02:06 -07:00
|
|
|
newOperands.push_back(allocOp.getOperand(dynamicDimPos));
|
2018-10-25 16:44:04 -07:00
|
|
|
}
|
|
|
|
dynamicDimPos++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create new memref type (which will have fewer dynamic dimensions).
|
2018-10-30 14:59:22 -07:00
|
|
|
auto newMemRefType = MemRefType::get(
|
|
|
|
newShapeConstants, memrefType.getElementType(),
|
|
|
|
memrefType.getAffineMaps(), memrefType.getMemorySpace());
|
|
|
|
assert(newOperands.size() == newMemRefType.getNumDynamicDims());
|
2018-10-25 16:44:04 -07:00
|
|
|
|
|
|
|
// Create and insert the alloc op for the new memref.
|
|
|
|
auto newAlloc =
|
2019-03-25 13:02:06 -07:00
|
|
|
rewriter.create<AllocOp>(allocOp.getLoc(), newMemRefType, newOperands);
|
2018-10-25 16:44:04 -07:00
|
|
|
// Insert a cast so we have the same type as the old alloc.
|
2019-03-25 13:02:06 -07:00
|
|
|
auto resultCast = rewriter.create<MemRefCastOp>(allocOp.getLoc(), newAlloc,
|
|
|
|
allocOp.getType());
|
2018-10-25 16:44:04 -07:00
|
|
|
|
2018-11-24 07:40:55 -08:00
|
|
|
rewriter.replaceOp(op, {resultCast}, droppedOperands);
|
2018-10-25 16:44:04 -07:00
|
|
|
}
|
|
|
|
};
|
2019-01-16 11:40:37 -08:00
|
|
|
|
2019-03-28 08:24:38 -07:00
|
|
|
/// Fold alloc operations with no uses. Alloc has side effects on the heap,
|
2019-01-16 11:40:37 -08:00
|
|
|
/// but can still be deleted if it has zero uses.
|
|
|
|
struct SimplifyDeadAlloc : public RewritePattern {
|
|
|
|
SimplifyDeadAlloc(MLIRContext *context)
|
|
|
|
: RewritePattern(AllocOp::getOperationName(), 1, context) {}
|
|
|
|
|
2019-03-28 08:24:38 -07:00
|
|
|
PatternMatchResult matchAndRewrite(Operation *op,
|
2019-03-25 11:53:03 -07:00
|
|
|
PatternRewriter &rewriter) const override {
|
|
|
|
// Check if the alloc'ed value has any uses.
|
2019-05-11 17:57:32 -07:00
|
|
|
auto alloc = cast<AllocOp>(op);
|
2019-03-25 13:02:06 -07:00
|
|
|
if (!alloc.use_empty())
|
2019-03-25 11:53:03 -07:00
|
|
|
return matchFailure();
|
2019-01-16 11:40:37 -08:00
|
|
|
|
2019-03-25 11:53:03 -07:00
|
|
|
// If it doesn't, we can eliminate it.
|
2019-01-16 11:40:37 -08:00
|
|
|
op->erase();
|
2019-03-25 11:53:03 -07:00
|
|
|
return matchSuccess();
|
2019-01-16 11:40:37 -08:00
|
|
|
}
|
|
|
|
};
|
2018-10-25 16:44:04 -07:00
|
|
|
} // end anonymous namespace.
|
|
|
|
|
2018-11-28 15:09:39 -08:00
|
|
|
void AllocOp::getCanonicalizationPatterns(OwningRewritePatternList &results,
|
2018-10-25 16:44:04 -07:00
|
|
|
MLIRContext *context) {
|
2019-05-18 13:23:38 -07:00
|
|
|
RewriteListBuilder<SimplifyAllocConst, SimplifyDeadAlloc>::build(results,
|
|
|
|
context);
|
2018-10-25 16:44:04 -07:00
|
|
|
}
|
|
|
|
|
2019-03-01 16:58:00 -08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// BranchOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2019-05-10 15:26:23 -07:00
|
|
|
static ParseResult parseBranchOp(OpAsmParser *parser, OperationState *result) {
|
2019-03-01 16:58:00 -08:00
|
|
|
Block *dest;
|
|
|
|
SmallVector<Value *, 4> destOperands;
|
|
|
|
if (parser->parseSuccessorAndUseList(dest, destOperands))
|
2019-05-06 22:01:31 -07:00
|
|
|
return failure();
|
2019-03-01 16:58:00 -08:00
|
|
|
result->addSuccessor(dest, destOperands);
|
2019-05-06 22:01:31 -07:00
|
|
|
return success();
|
2019-03-01 16:58:00 -08:00
|
|
|
}
|
|
|
|
|
2019-05-14 08:27:32 -07:00
|
|
|
static void print(OpAsmPrinter *p, BranchOp op) {
|
2019-03-01 16:58:00 -08:00
|
|
|
*p << "br ";
|
2019-05-10 15:26:23 -07:00
|
|
|
p->printSuccessorAndUseList(op.getOperation(), 0);
|
2019-03-01 16:58:00 -08:00
|
|
|
}
|
|
|
|
|
2019-03-26 17:05:09 -07:00
|
|
|
Block *BranchOp::getDest() { return getOperation()->getSuccessor(0); }
|
2019-03-01 16:58:00 -08:00
|
|
|
|
|
|
|
void BranchOp::setDest(Block *block) {
|
2019-03-26 17:05:09 -07:00
|
|
|
return getOperation()->setSuccessor(block, 0);
|
2019-03-01 16:58:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void BranchOp::eraseOperand(unsigned index) {
|
2019-03-26 17:05:09 -07:00
|
|
|
getOperation()->eraseSuccessorOperand(0, index);
|
2019-03-01 16:58:00 -08:00
|
|
|
}
|
|
|
|
|
2018-08-09 12:28:58 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
2018-08-21 17:55:22 -07:00
|
|
|
// CallOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2019-05-10 15:27:34 -07:00
|
|
|
static ParseResult parseCallOp(OpAsmParser *parser, OperationState *result) {
|
2018-08-21 17:55:22 -07:00
|
|
|
StringRef calleeName;
|
|
|
|
llvm::SMLoc calleeLoc;
|
2018-10-30 14:59:22 -07:00
|
|
|
FunctionType calleeType;
|
2018-08-21 17:55:22 -07:00
|
|
|
SmallVector<OpAsmParser::OperandType, 4> operands;
|
|
|
|
if (parser->parseFunctionName(calleeName, calleeLoc) ||
|
|
|
|
parser->parseOperandList(operands, /*requiredOperandCount=*/-1,
|
|
|
|
OpAsmParser::Delimiter::Paren) ||
|
|
|
|
parser->parseOptionalAttributeDict(result->attributes) ||
|
|
|
|
parser->parseColonType(calleeType) ||
|
2018-10-30 14:59:22 -07:00
|
|
|
parser->addTypesToList(calleeType.getResults(), result->types) ||
|
|
|
|
parser->resolveOperands(operands, calleeType.getInputs(), calleeLoc,
|
2018-08-21 17:55:22 -07:00
|
|
|
result->operands))
|
2019-05-06 22:01:31 -07:00
|
|
|
return failure();
|
2018-08-21 17:55:22 -07:00
|
|
|
|
2019-05-22 13:41:23 -07:00
|
|
|
result->addAttribute("callee",
|
|
|
|
parser->getBuilder().getFunctionAttr(calleeName));
|
2019-05-06 22:01:31 -07:00
|
|
|
return success();
|
2018-08-21 17:55:22 -07:00
|
|
|
}
|
|
|
|
|
2019-05-14 08:27:32 -07:00
|
|
|
static void print(OpAsmPrinter *p, CallOp op) {
|
2019-05-23 17:01:16 -07:00
|
|
|
*p << "call " << op.getAttr("callee") << '(';
|
2019-05-10 15:27:34 -07:00
|
|
|
p->printOperands(op.getOperands());
|
2018-08-21 17:55:22 -07:00
|
|
|
*p << ')';
|
2019-05-10 15:27:34 -07:00
|
|
|
p->printOptionalAttrDict(op.getAttrs(), /*elidedAttrs=*/{"callee"});
|
2019-05-23 17:01:16 -07:00
|
|
|
*p << " : ";
|
|
|
|
p->printType(op.getCalleeType());
|
2018-08-21 17:55:22 -07:00
|
|
|
}
|
|
|
|
|
2019-05-10 15:27:34 -07:00
|
|
|
static LogicalResult verify(CallOp op) {
|
2018-08-21 17:55:22 -07:00
|
|
|
// Check that the callee attribute was specified.
|
2019-05-10 15:27:34 -07:00
|
|
|
auto fnAttr = op.getAttrOfType<FunctionAttr>("callee");
|
2018-08-21 17:55:22 -07:00
|
|
|
if (!fnAttr)
|
2019-05-10 15:27:34 -07:00
|
|
|
return op.emitOpError("requires a 'callee' function attribute");
|
2019-05-22 13:41:23 -07:00
|
|
|
auto *fn = op.getOperation()->getFunction()->getModule()->getNamedFunction(
|
|
|
|
fnAttr.getValue());
|
|
|
|
if (!fn)
|
|
|
|
return op.emitOpError() << "'" << fnAttr.getValue()
|
|
|
|
<< "' does not reference a valid function";
|
2018-08-21 17:55:22 -07:00
|
|
|
|
|
|
|
// Verify that the operand and result types match the callee.
|
2019-05-22 13:41:23 -07:00
|
|
|
auto fnType = fn->getType();
|
2019-05-10 15:27:34 -07:00
|
|
|
if (fnType.getNumInputs() != op.getNumOperands())
|
|
|
|
return op.emitOpError("incorrect number of operands for callee");
|
2018-08-21 17:55:22 -07:00
|
|
|
|
2019-05-10 15:27:34 -07:00
|
|
|
for (unsigned i = 0, e = fnType.getNumInputs(); i != e; ++i)
|
|
|
|
if (op.getOperand(i)->getType() != fnType.getInput(i))
|
|
|
|
return op.emitOpError("operand type mismatch");
|
2018-08-21 17:55:22 -07:00
|
|
|
|
2019-05-10 15:27:34 -07:00
|
|
|
if (fnType.getNumResults() != op.getNumResults())
|
|
|
|
return op.emitOpError("incorrect number of results for callee");
|
2018-08-21 17:55:22 -07:00
|
|
|
|
2019-05-10 15:27:34 -07:00
|
|
|
for (unsigned i = 0, e = fnType.getNumResults(); i != e; ++i)
|
|
|
|
if (op.getResult(i)->getType() != fnType.getResult(i))
|
|
|
|
return op.emitOpError("result type mismatch");
|
2018-08-21 17:55:22 -07:00
|
|
|
|
2019-04-02 13:09:34 -07:00
|
|
|
return success();
|
2018-08-21 17:55:22 -07:00
|
|
|
}
|
|
|
|
|
2019-05-23 17:01:16 -07:00
|
|
|
FunctionType CallOp::getCalleeType() {
|
2019-05-24 13:28:55 -07:00
|
|
|
SmallVector<Type, 4> resultTypes(getResultTypes());
|
|
|
|
SmallVector<Type, 8> argTypes(getOperandTypes());
|
2019-05-23 17:01:16 -07:00
|
|
|
return FunctionType::get(argTypes, resultTypes, getContext());
|
2019-05-22 13:41:23 -07:00
|
|
|
}
|
|
|
|
|
2018-08-21 17:55:22 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// CallIndirectOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
2019-01-29 18:08:28 -08:00
|
|
|
namespace {
|
|
|
|
/// Fold indirect calls that have a constant function as the callee operand.
|
|
|
|
struct SimplifyIndirectCallWithKnownCallee : public RewritePattern {
|
|
|
|
SimplifyIndirectCallWithKnownCallee(MLIRContext *context)
|
|
|
|
: RewritePattern(CallIndirectOp::getOperationName(), 1, context) {}
|
|
|
|
|
2019-03-28 08:24:38 -07:00
|
|
|
PatternMatchResult matchAndRewrite(Operation *op,
|
2019-03-25 11:53:03 -07:00
|
|
|
PatternRewriter &rewriter) const override {
|
2019-05-11 17:57:32 -07:00
|
|
|
auto indirectCall = cast<CallIndirectOp>(op);
|
2019-01-29 18:08:28 -08:00
|
|
|
|
|
|
|
// Check that the callee is a constant operation.
|
2019-03-25 11:53:03 -07:00
|
|
|
Attribute callee;
|
2019-03-25 13:02:06 -07:00
|
|
|
if (!matchPattern(indirectCall.getCallee(), m_Constant(&callee)))
|
2019-01-29 18:08:28 -08:00
|
|
|
return matchFailure();
|
|
|
|
|
|
|
|
// Check that the constant callee is a function.
|
2019-03-25 11:53:03 -07:00
|
|
|
FunctionAttr calledFn = callee.dyn_cast<FunctionAttr>();
|
|
|
|
if (!calledFn)
|
|
|
|
return matchFailure();
|
2019-01-29 18:08:28 -08:00
|
|
|
|
|
|
|
// Replace with a direct call.
|
2019-05-22 13:41:23 -07:00
|
|
|
SmallVector<Type, 8> callResults(op->getResultTypes());
|
2019-03-25 13:02:06 -07:00
|
|
|
SmallVector<Value *, 8> callOperands(indirectCall.getArgOperands());
|
2019-05-22 13:41:23 -07:00
|
|
|
rewriter.replaceOpWithNewOp<CallOp>(op, calledFn.getValue(), callResults,
|
|
|
|
callOperands);
|
2019-03-25 11:53:03 -07:00
|
|
|
return matchSuccess();
|
2019-01-29 18:08:28 -08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
} // end anonymous namespace.
|
2018-08-21 17:55:22 -07:00
|
|
|
|
2019-05-10 15:27:34 -07:00
|
|
|
static ParseResult parseCallIndirectOp(OpAsmParser *parser,
|
|
|
|
OperationState *result) {
|
2018-10-30 14:59:22 -07:00
|
|
|
FunctionType calleeType;
|
2018-08-21 17:55:22 -07:00
|
|
|
OpAsmParser::OperandType callee;
|
|
|
|
llvm::SMLoc operandsLoc;
|
|
|
|
SmallVector<OpAsmParser::OperandType, 4> operands;
|
2019-05-06 22:01:31 -07:00
|
|
|
return failure(
|
|
|
|
parser->parseOperand(callee) ||
|
|
|
|
parser->getCurrentLocation(&operandsLoc) ||
|
|
|
|
parser->parseOperandList(operands, /*requiredOperandCount=*/-1,
|
|
|
|
OpAsmParser::Delimiter::Paren) ||
|
|
|
|
parser->parseOptionalAttributeDict(result->attributes) ||
|
|
|
|
parser->parseColonType(calleeType) ||
|
|
|
|
parser->resolveOperand(callee, calleeType, result->operands) ||
|
|
|
|
parser->resolveOperands(operands, calleeType.getInputs(), operandsLoc,
|
|
|
|
result->operands) ||
|
|
|
|
parser->addTypesToList(calleeType.getResults(), result->types));
|
2018-08-21 17:55:22 -07:00
|
|
|
}
|
|
|
|
|
2019-05-14 08:27:32 -07:00
|
|
|
static void print(OpAsmPrinter *p, CallIndirectOp op) {
|
2018-08-21 17:55:22 -07:00
|
|
|
*p << "call_indirect ";
|
2019-05-10 15:27:34 -07:00
|
|
|
p->printOperand(op.getCallee());
|
2018-08-21 17:55:22 -07:00
|
|
|
*p << '(';
|
2019-05-10 15:27:34 -07:00
|
|
|
auto operandRange = op.getOperands();
|
2018-08-21 17:55:22 -07:00
|
|
|
p->printOperands(++operandRange.begin(), operandRange.end());
|
|
|
|
*p << ')';
|
2019-05-10 15:27:34 -07:00
|
|
|
p->printOptionalAttrDict(op.getAttrs(), /*elidedAttrs=*/{"callee"});
|
|
|
|
*p << " : " << op.getCallee()->getType();
|
2018-08-21 17:55:22 -07:00
|
|
|
}
|
|
|
|
|
2019-05-10 15:27:34 -07:00
|
|
|
static LogicalResult verify(CallIndirectOp op) {
|
2018-08-21 17:55:22 -07:00
|
|
|
// The callee must be a function.
|
2019-05-10 15:27:34 -07:00
|
|
|
auto fnType = op.getCallee()->getType().dyn_cast<FunctionType>();
|
2018-08-21 17:55:22 -07:00
|
|
|
if (!fnType)
|
2019-05-10 15:27:34 -07:00
|
|
|
return op.emitOpError("callee must have function type");
|
2018-08-21 17:55:22 -07:00
|
|
|
|
|
|
|
// Verify that the operand and result types match the callee.
|
2019-05-10 15:27:34 -07:00
|
|
|
if (fnType.getNumInputs() != op.getNumOperands() - 1)
|
|
|
|
return op.emitOpError("incorrect number of operands for callee");
|
2018-08-21 17:55:22 -07:00
|
|
|
|
2019-05-10 15:27:34 -07:00
|
|
|
for (unsigned i = 0, e = fnType.getNumInputs(); i != e; ++i)
|
|
|
|
if (op.getOperand(i + 1)->getType() != fnType.getInput(i))
|
|
|
|
return op.emitOpError("operand type mismatch");
|
2018-08-21 17:55:22 -07:00
|
|
|
|
2019-05-10 15:27:34 -07:00
|
|
|
if (fnType.getNumResults() != op.getNumResults())
|
|
|
|
return op.emitOpError("incorrect number of results for callee");
|
2018-08-21 17:55:22 -07:00
|
|
|
|
2019-05-10 15:27:34 -07:00
|
|
|
for (unsigned i = 0, e = fnType.getNumResults(); i != e; ++i)
|
|
|
|
if (op.getResult(i)->getType() != fnType.getResult(i))
|
|
|
|
return op.emitOpError("result type mismatch");
|
2018-08-21 17:55:22 -07:00
|
|
|
|
2019-04-02 13:09:34 -07:00
|
|
|
return success();
|
2018-08-21 17:55:22 -07:00
|
|
|
}
|
|
|
|
|
2019-01-29 18:08:28 -08:00
|
|
|
void CallIndirectOp::getCanonicalizationPatterns(
|
|
|
|
OwningRewritePatternList &results, MLIRContext *context) {
|
|
|
|
results.push_back(
|
2019-03-19 08:45:06 -07:00
|
|
|
llvm::make_unique<SimplifyIndirectCallWithKnownCallee>(context));
|
2019-01-29 18:08:28 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
2019-05-06 17:51:08 -07:00
|
|
|
// General helpers for comparison ops
|
2019-01-29 18:08:28 -08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2018-11-08 04:02:00 -08:00
|
|
|
// Return the type of the same shape (scalar, vector or tensor) containing i1.
|
2019-01-15 09:30:39 -08:00
|
|
|
static Type getCheckedI1SameShape(Builder *build, Type type) {
|
2018-11-28 11:49:26 -08:00
|
|
|
auto i1Type = build->getI1Type();
|
2018-12-05 04:31:59 -08:00
|
|
|
if (type.isIntOrIndexOrFloat())
|
2018-11-08 04:02:00 -08:00
|
|
|
return i1Type;
|
|
|
|
if (auto tensorType = type.dyn_cast<RankedTensorType>())
|
|
|
|
return build->getTensorType(tensorType.getShape(), i1Type);
|
|
|
|
if (auto tensorType = type.dyn_cast<UnrankedTensorType>())
|
|
|
|
return build->getTensorType(i1Type);
|
|
|
|
if (auto vectorType = type.dyn_cast<VectorType>())
|
|
|
|
return build->getVectorType(vectorType.getShape(), i1Type);
|
2019-01-15 09:30:39 -08:00
|
|
|
return Type();
|
|
|
|
}
|
2018-11-08 04:02:00 -08:00
|
|
|
|
2019-01-15 09:30:39 -08:00
|
|
|
static Type getI1SameShape(Builder *build, Type type) {
|
|
|
|
Type res = getCheckedI1SameShape(build, type);
|
|
|
|
assert(res && "expected type with valid i1 shape");
|
|
|
|
return res;
|
2018-11-08 04:02:00 -08:00
|
|
|
}
|
|
|
|
|
2019-05-06 17:51:08 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// CmpIOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
// Returns an array of mnemonics for CmpIPredicates indexed by values thereof.
|
|
|
|
static inline const char *const *getCmpIPredicateNames() {
|
|
|
|
static const char *predicateNames[]{
|
2018-11-08 04:02:00 -08:00
|
|
|
/*EQ*/ "eq",
|
|
|
|
/*NE*/ "ne",
|
|
|
|
/*SLT*/ "slt",
|
|
|
|
/*SLE*/ "sle",
|
|
|
|
/*SGT*/ "sgt",
|
|
|
|
/*SGE*/ "sge",
|
|
|
|
/*ULT*/ "ult",
|
|
|
|
/*ULE*/ "ule",
|
|
|
|
/*UGT*/ "ugt",
|
2019-05-06 17:51:08 -07:00
|
|
|
/*UGE*/ "uge",
|
|
|
|
};
|
|
|
|
static_assert(std::extent<decltype(predicateNames)>::value ==
|
|
|
|
(size_t)CmpIPredicate::NumPredicates,
|
|
|
|
"wrong number of predicate names");
|
2018-11-08 04:02:00 -08:00
|
|
|
return predicateNames;
|
2019-04-04 13:25:29 -07:00
|
|
|
}
|
2018-11-08 04:02:00 -08:00
|
|
|
|
|
|
|
// Returns a value of the predicate corresponding to the given mnemonic.
|
|
|
|
// Returns NumPredicates (one-past-end) if there is no such mnemonic.
|
|
|
|
CmpIPredicate CmpIOp::getPredicateByName(StringRef name) {
|
|
|
|
return llvm::StringSwitch<CmpIPredicate>(name)
|
|
|
|
.Case("eq", CmpIPredicate::EQ)
|
|
|
|
.Case("ne", CmpIPredicate::NE)
|
|
|
|
.Case("slt", CmpIPredicate::SLT)
|
|
|
|
.Case("sle", CmpIPredicate::SLE)
|
|
|
|
.Case("sgt", CmpIPredicate::SGT)
|
|
|
|
.Case("sge", CmpIPredicate::SGE)
|
|
|
|
.Case("ult", CmpIPredicate::ULT)
|
|
|
|
.Case("ule", CmpIPredicate::ULE)
|
|
|
|
.Case("ugt", CmpIPredicate::UGT)
|
|
|
|
.Case("uge", CmpIPredicate::UGE)
|
|
|
|
.Default(CmpIPredicate::NumPredicates);
|
|
|
|
}
|
|
|
|
|
2019-05-24 18:01:20 -07:00
|
|
|
static void buildCmpIOp(Builder *build, OperationState *result,
|
|
|
|
CmpIPredicate predicate, Value *lhs, Value *rhs) {
|
2018-11-08 04:02:00 -08:00
|
|
|
result->addOperands({lhs, rhs});
|
|
|
|
result->types.push_back(getI1SameShape(build, lhs->getType()));
|
2019-05-06 17:51:08 -07:00
|
|
|
result->addAttribute(
|
2019-05-24 18:01:20 -07:00
|
|
|
CmpIOp::getPredicateAttrName(),
|
2019-05-06 17:51:08 -07:00
|
|
|
build->getI64IntegerAttr(static_cast<int64_t>(predicate)));
|
2018-11-08 04:02:00 -08:00
|
|
|
}
|
|
|
|
|
2019-05-24 18:01:20 -07:00
|
|
|
static ParseResult parseCmpIOp(OpAsmParser *parser, OperationState *result) {
|
2018-11-08 04:02:00 -08:00
|
|
|
SmallVector<OpAsmParser::OperandType, 2> ops;
|
|
|
|
SmallVector<NamedAttribute, 4> attrs;
|
2019-01-15 12:33:09 -08:00
|
|
|
Attribute predicateNameAttr;
|
2018-11-08 04:02:00 -08:00
|
|
|
Type type;
|
2019-05-24 18:01:20 -07:00
|
|
|
if (parser->parseAttribute(predicateNameAttr, CmpIOp::getPredicateAttrName(),
|
2018-11-08 04:02:00 -08:00
|
|
|
attrs) ||
|
|
|
|
parser->parseComma() || parser->parseOperandList(ops, 2) ||
|
|
|
|
parser->parseOptionalAttributeDict(attrs) ||
|
|
|
|
parser->parseColonType(type) ||
|
|
|
|
parser->resolveOperands(ops, type, result->operands))
|
2019-05-06 22:01:31 -07:00
|
|
|
return failure();
|
2018-11-08 04:02:00 -08:00
|
|
|
|
2019-01-15 12:33:09 -08:00
|
|
|
if (!predicateNameAttr.isa<StringAttr>())
|
|
|
|
return parser->emitError(parser->getNameLoc(),
|
|
|
|
"expected string comparison predicate attribute");
|
|
|
|
|
2018-11-08 04:02:00 -08:00
|
|
|
// Rewrite string attribute to an enum value.
|
2019-01-15 12:33:09 -08:00
|
|
|
StringRef predicateName = predicateNameAttr.cast<StringAttr>().getValue();
|
2019-05-24 18:01:20 -07:00
|
|
|
auto predicate = CmpIOp::getPredicateByName(predicateName);
|
2018-11-08 04:02:00 -08:00
|
|
|
if (predicate == CmpIPredicate::NumPredicates)
|
2019-05-08 22:42:58 -07:00
|
|
|
return parser->emitError(parser->getNameLoc())
|
|
|
|
<< "unknown comparison predicate \"" << predicateName << "\"";
|
2019-01-15 09:30:39 -08:00
|
|
|
|
2018-11-15 17:53:51 -08:00
|
|
|
auto builder = parser->getBuilder();
|
2019-01-15 09:30:39 -08:00
|
|
|
Type i1Type = getCheckedI1SameShape(&builder, type);
|
|
|
|
if (!i1Type)
|
|
|
|
return parser->emitError(parser->getNameLoc(),
|
|
|
|
"expected type with valid i1 shape");
|
|
|
|
|
2018-12-26 11:48:58 -08:00
|
|
|
attrs[0].second = builder.getI64IntegerAttr(static_cast<int64_t>(predicate));
|
2018-11-08 04:02:00 -08:00
|
|
|
result->attributes = attrs;
|
|
|
|
|
2019-01-15 09:30:39 -08:00
|
|
|
result->addTypes({i1Type});
|
2019-05-06 22:01:31 -07:00
|
|
|
return success();
|
2018-11-08 04:02:00 -08:00
|
|
|
}
|
|
|
|
|
2019-05-24 18:01:20 -07:00
|
|
|
static void print(OpAsmPrinter *p, CmpIOp op) {
|
2019-03-02 18:03:03 -08:00
|
|
|
*p << "cmpi ";
|
2018-11-08 04:02:00 -08:00
|
|
|
|
2018-11-12 06:33:22 -08:00
|
|
|
auto predicateValue =
|
2019-05-24 18:01:20 -07:00
|
|
|
op.getAttrOfType<IntegerAttr>(CmpIOp::getPredicateAttrName()).getInt();
|
2018-11-08 04:02:00 -08:00
|
|
|
assert(predicateValue >= static_cast<int>(CmpIPredicate::FirstValidValue) &&
|
|
|
|
predicateValue < static_cast<int>(CmpIPredicate::NumPredicates) &&
|
|
|
|
"unknown predicate index");
|
2019-05-24 18:01:20 -07:00
|
|
|
Builder b(op.getContext());
|
2018-11-08 04:02:00 -08:00
|
|
|
auto predicateStringAttr =
|
2019-05-06 17:51:08 -07:00
|
|
|
b.getStringAttr(getCmpIPredicateNames()[predicateValue]);
|
2018-11-08 04:02:00 -08:00
|
|
|
p->printAttribute(predicateStringAttr);
|
|
|
|
|
|
|
|
*p << ", ";
|
2019-05-24 18:01:20 -07:00
|
|
|
p->printOperand(op.lhs());
|
2018-11-08 04:02:00 -08:00
|
|
|
*p << ", ";
|
2019-05-24 18:01:20 -07:00
|
|
|
p->printOperand(op.rhs());
|
|
|
|
p->printOptionalAttrDict(op.getAttrs(),
|
|
|
|
/*elidedAttrs=*/{CmpIOp::getPredicateAttrName()});
|
|
|
|
*p << " : " << op.lhs()->getType();
|
2018-11-08 04:02:00 -08:00
|
|
|
}
|
|
|
|
|
2019-05-24 18:01:20 -07:00
|
|
|
static LogicalResult verify(CmpIOp op) {
|
|
|
|
auto predicateAttr =
|
|
|
|
op.getAttrOfType<IntegerAttr>(CmpIOp::getPredicateAttrName());
|
2018-11-08 04:02:00 -08:00
|
|
|
if (!predicateAttr)
|
2019-05-24 18:01:20 -07:00
|
|
|
return op.emitOpError("requires an integer attribute named 'predicate'");
|
2018-11-12 06:33:22 -08:00
|
|
|
auto predicate = predicateAttr.getInt();
|
2018-11-08 04:02:00 -08:00
|
|
|
if (predicate < (int64_t)CmpIPredicate::FirstValidValue ||
|
|
|
|
predicate >= (int64_t)CmpIPredicate::NumPredicates)
|
2019-05-24 18:01:20 -07:00
|
|
|
return op.emitOpError("'predicate' attribute value out of range");
|
2018-11-08 04:02:00 -08:00
|
|
|
|
2019-04-02 13:09:34 -07:00
|
|
|
return success();
|
2018-11-08 04:02:00 -08:00
|
|
|
}
|
|
|
|
|
2019-01-06 14:09:15 -08:00
|
|
|
// Compute `lhs` `pred` `rhs`, where `pred` is one of the known integer
|
|
|
|
// comparison predicates.
|
|
|
|
static bool applyCmpPredicate(CmpIPredicate predicate, const APInt &lhs,
|
|
|
|
const APInt &rhs) {
|
|
|
|
switch (predicate) {
|
|
|
|
case CmpIPredicate::EQ:
|
|
|
|
return lhs.eq(rhs);
|
|
|
|
case CmpIPredicate::NE:
|
|
|
|
return lhs.ne(rhs);
|
|
|
|
case CmpIPredicate::SLT:
|
|
|
|
return lhs.slt(rhs);
|
|
|
|
case CmpIPredicate::SLE:
|
|
|
|
return lhs.sle(rhs);
|
|
|
|
case CmpIPredicate::SGT:
|
|
|
|
return lhs.sgt(rhs);
|
|
|
|
case CmpIPredicate::SGE:
|
|
|
|
return lhs.sge(rhs);
|
|
|
|
case CmpIPredicate::ULT:
|
|
|
|
return lhs.ult(rhs);
|
|
|
|
case CmpIPredicate::ULE:
|
|
|
|
return lhs.ule(rhs);
|
|
|
|
case CmpIPredicate::UGT:
|
|
|
|
return lhs.ugt(rhs);
|
|
|
|
case CmpIPredicate::UGE:
|
|
|
|
return lhs.uge(rhs);
|
|
|
|
default:
|
|
|
|
llvm_unreachable("unknown comparison predicate");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Constant folding hook for comparisons.
|
2019-05-16 12:51:45 -07:00
|
|
|
OpFoldResult CmpIOp::fold(ArrayRef<Attribute> operands) {
|
2019-01-06 14:09:15 -08:00
|
|
|
assert(operands.size() == 2 && "cmpi takes two arguments");
|
|
|
|
|
|
|
|
auto lhs = operands.front().dyn_cast_or_null<IntegerAttr>();
|
|
|
|
auto rhs = operands.back().dyn_cast_or_null<IntegerAttr>();
|
|
|
|
if (!lhs || !rhs)
|
|
|
|
return {};
|
|
|
|
|
|
|
|
auto val = applyCmpPredicate(getPredicate(), lhs.getValue(), rhs.getValue());
|
2019-05-16 12:51:45 -07:00
|
|
|
return IntegerAttr::get(IntegerType::get(1, getContext()), APInt(1, val));
|
2019-01-06 14:09:15 -08:00
|
|
|
}
|
|
|
|
|
2019-05-06 17:51:08 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// CmpFOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
// Returns an array of mnemonics for CmpFPredicates indexed by values thereof.
|
|
|
|
static inline const char *const *getCmpFPredicateNames() {
|
|
|
|
static const char *predicateNames[] = {
|
|
|
|
/*FALSE*/ "false",
|
|
|
|
/*OEQ*/ "oeq",
|
|
|
|
/*OGT*/ "ogt",
|
|
|
|
/*OGE*/ "oge",
|
|
|
|
/*OLT*/ "olt",
|
|
|
|
/*OLE*/ "ole",
|
|
|
|
/*ONE*/ "one",
|
|
|
|
/*ORD*/ "ord",
|
|
|
|
/*UEQ*/ "ueq",
|
|
|
|
/*UGT*/ "ugt",
|
|
|
|
/*UGE*/ "uge",
|
|
|
|
/*ULT*/ "ult",
|
|
|
|
/*ULE*/ "ule",
|
|
|
|
/*UNE*/ "une",
|
|
|
|
/*UNO*/ "uno",
|
|
|
|
/*TRUE*/ "true",
|
|
|
|
};
|
|
|
|
static_assert(std::extent<decltype(predicateNames)>::value ==
|
|
|
|
(size_t)CmpFPredicate::NumPredicates,
|
|
|
|
"wrong number of predicate names");
|
|
|
|
return predicateNames;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns a value of the predicate corresponding to the given mnemonic.
|
|
|
|
// Returns NumPredicates (one-past-end) if there is no such mnemonic.
|
|
|
|
CmpFPredicate CmpFOp::getPredicateByName(StringRef name) {
|
|
|
|
return llvm::StringSwitch<CmpFPredicate>(name)
|
|
|
|
.Case("false", CmpFPredicate::FALSE)
|
|
|
|
.Case("oeq", CmpFPredicate::OEQ)
|
|
|
|
.Case("ogt", CmpFPredicate::OGT)
|
|
|
|
.Case("oge", CmpFPredicate::OGE)
|
|
|
|
.Case("olt", CmpFPredicate::OLT)
|
|
|
|
.Case("ole", CmpFPredicate::OLE)
|
|
|
|
.Case("one", CmpFPredicate::ONE)
|
|
|
|
.Case("ord", CmpFPredicate::ORD)
|
|
|
|
.Case("ueq", CmpFPredicate::UEQ)
|
|
|
|
.Case("ugt", CmpFPredicate::UGT)
|
|
|
|
.Case("uge", CmpFPredicate::UGE)
|
|
|
|
.Case("ult", CmpFPredicate::ULT)
|
|
|
|
.Case("ule", CmpFPredicate::ULE)
|
|
|
|
.Case("une", CmpFPredicate::UNE)
|
|
|
|
.Case("uno", CmpFPredicate::UNO)
|
|
|
|
.Case("true", CmpFPredicate::TRUE)
|
|
|
|
.Default(CmpFPredicate::NumPredicates);
|
|
|
|
}
|
|
|
|
|
2019-05-24 18:01:20 -07:00
|
|
|
static void buildCmpFOp(Builder *build, OperationState *result,
|
|
|
|
CmpFPredicate predicate, Value *lhs, Value *rhs) {
|
2019-05-06 17:51:08 -07:00
|
|
|
result->addOperands({lhs, rhs});
|
|
|
|
result->types.push_back(getI1SameShape(build, lhs->getType()));
|
|
|
|
result->addAttribute(
|
2019-05-24 18:01:20 -07:00
|
|
|
CmpFOp::getPredicateAttrName(),
|
2019-05-06 17:51:08 -07:00
|
|
|
build->getI64IntegerAttr(static_cast<int64_t>(predicate)));
|
|
|
|
}
|
|
|
|
|
2019-05-24 18:01:20 -07:00
|
|
|
static ParseResult parseCmpFOp(OpAsmParser *parser, OperationState *result) {
|
2019-05-06 17:51:08 -07:00
|
|
|
SmallVector<OpAsmParser::OperandType, 2> ops;
|
|
|
|
SmallVector<NamedAttribute, 4> attrs;
|
|
|
|
Attribute predicateNameAttr;
|
|
|
|
Type type;
|
2019-05-24 18:01:20 -07:00
|
|
|
if (parser->parseAttribute(predicateNameAttr, CmpFOp::getPredicateAttrName(),
|
2019-05-06 17:51:08 -07:00
|
|
|
attrs) ||
|
|
|
|
parser->parseComma() || parser->parseOperandList(ops, 2) ||
|
|
|
|
parser->parseOptionalAttributeDict(attrs) ||
|
|
|
|
parser->parseColonType(type) ||
|
|
|
|
parser->resolveOperands(ops, type, result->operands))
|
2019-05-06 22:01:31 -07:00
|
|
|
return failure();
|
2019-05-06 17:51:08 -07:00
|
|
|
|
|
|
|
if (!predicateNameAttr.isa<StringAttr>())
|
|
|
|
return parser->emitError(parser->getNameLoc(),
|
|
|
|
"expected string comparison predicate attribute");
|
|
|
|
|
|
|
|
// Rewrite string attribute to an enum value.
|
|
|
|
StringRef predicateName = predicateNameAttr.cast<StringAttr>().getValue();
|
2019-05-24 18:01:20 -07:00
|
|
|
auto predicate = CmpFOp::getPredicateByName(predicateName);
|
2019-05-06 17:51:08 -07:00
|
|
|
if (predicate == CmpFPredicate::NumPredicates)
|
|
|
|
return parser->emitError(parser->getNameLoc(),
|
|
|
|
"unknown comparison predicate \"" + predicateName +
|
|
|
|
"\"");
|
|
|
|
|
|
|
|
auto builder = parser->getBuilder();
|
|
|
|
Type i1Type = getCheckedI1SameShape(&builder, type);
|
|
|
|
if (!i1Type)
|
|
|
|
return parser->emitError(parser->getNameLoc(),
|
|
|
|
"expected type with valid i1 shape");
|
|
|
|
|
|
|
|
attrs[0].second = builder.getI64IntegerAttr(static_cast<int64_t>(predicate));
|
|
|
|
result->attributes = attrs;
|
|
|
|
|
|
|
|
result->addTypes({i1Type});
|
2019-05-06 22:01:31 -07:00
|
|
|
return success();
|
2019-05-06 17:51:08 -07:00
|
|
|
}
|
|
|
|
|
2019-05-24 18:01:20 -07:00
|
|
|
static void print(OpAsmPrinter *p, CmpFOp op) {
|
2019-05-06 17:51:08 -07:00
|
|
|
*p << "cmpf ";
|
|
|
|
|
|
|
|
auto predicateValue =
|
2019-05-24 18:01:20 -07:00
|
|
|
op.getAttrOfType<IntegerAttr>(CmpFOp::getPredicateAttrName()).getInt();
|
2019-05-06 17:51:08 -07:00
|
|
|
assert(predicateValue >= static_cast<int>(CmpFPredicate::FirstValidValue) &&
|
|
|
|
predicateValue < static_cast<int>(CmpFPredicate::NumPredicates) &&
|
|
|
|
"unknown predicate index");
|
2019-05-24 18:01:20 -07:00
|
|
|
Builder b(op.getContext());
|
2019-05-06 17:51:08 -07:00
|
|
|
auto predicateStringAttr =
|
|
|
|
b.getStringAttr(getCmpFPredicateNames()[predicateValue]);
|
|
|
|
p->printAttribute(predicateStringAttr);
|
|
|
|
|
|
|
|
*p << ", ";
|
2019-05-24 18:01:20 -07:00
|
|
|
p->printOperand(op.lhs());
|
2019-05-06 17:51:08 -07:00
|
|
|
*p << ", ";
|
2019-05-24 18:01:20 -07:00
|
|
|
p->printOperand(op.rhs());
|
|
|
|
p->printOptionalAttrDict(op.getAttrs(),
|
|
|
|
/*elidedAttrs=*/{CmpFOp::getPredicateAttrName()});
|
|
|
|
*p << " : " << op.lhs()->getType();
|
2019-05-06 17:51:08 -07:00
|
|
|
}
|
|
|
|
|
2019-05-24 18:01:20 -07:00
|
|
|
static LogicalResult verify(CmpFOp op) {
|
|
|
|
auto predicateAttr =
|
|
|
|
op.getAttrOfType<IntegerAttr>(CmpFOp::getPredicateAttrName());
|
2019-05-06 17:51:08 -07:00
|
|
|
if (!predicateAttr)
|
2019-05-24 18:01:20 -07:00
|
|
|
return op.emitOpError("requires an integer attribute named 'predicate'");
|
2019-05-06 17:51:08 -07:00
|
|
|
auto predicate = predicateAttr.getInt();
|
|
|
|
if (predicate < (int64_t)CmpFPredicate::FirstValidValue ||
|
|
|
|
predicate >= (int64_t)CmpFPredicate::NumPredicates)
|
2019-05-24 18:01:20 -07:00
|
|
|
return op.emitOpError("'predicate' attribute value out of range");
|
2019-05-06 17:51:08 -07:00
|
|
|
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compute `lhs` `pred` `rhs`, where `pred` is one of the known floating point
|
|
|
|
// comparison predicates.
|
|
|
|
static bool applyCmpPredicate(CmpFPredicate predicate, const APFloat &lhs,
|
|
|
|
const APFloat &rhs) {
|
|
|
|
auto cmpResult = lhs.compare(rhs);
|
|
|
|
switch (predicate) {
|
|
|
|
case CmpFPredicate::FALSE:
|
|
|
|
return false;
|
|
|
|
case CmpFPredicate::OEQ:
|
|
|
|
return cmpResult == APFloat::cmpEqual;
|
|
|
|
case CmpFPredicate::OGT:
|
|
|
|
return cmpResult == APFloat::cmpGreaterThan;
|
|
|
|
case CmpFPredicate::OGE:
|
|
|
|
return cmpResult == APFloat::cmpGreaterThan ||
|
|
|
|
cmpResult == APFloat::cmpEqual;
|
|
|
|
case CmpFPredicate::OLT:
|
|
|
|
return cmpResult == APFloat::cmpLessThan;
|
|
|
|
case CmpFPredicate::OLE:
|
|
|
|
return cmpResult == APFloat::cmpLessThan || cmpResult == APFloat::cmpEqual;
|
|
|
|
case CmpFPredicate::ONE:
|
|
|
|
return cmpResult != APFloat::cmpUnordered && cmpResult != APFloat::cmpEqual;
|
|
|
|
case CmpFPredicate::ORD:
|
|
|
|
return cmpResult != APFloat::cmpUnordered;
|
|
|
|
case CmpFPredicate::UEQ:
|
|
|
|
return cmpResult == APFloat::cmpUnordered || cmpResult == APFloat::cmpEqual;
|
|
|
|
case CmpFPredicate::UGT:
|
|
|
|
return cmpResult == APFloat::cmpUnordered ||
|
|
|
|
cmpResult == APFloat::cmpGreaterThan;
|
|
|
|
case CmpFPredicate::UGE:
|
|
|
|
return cmpResult == APFloat::cmpUnordered ||
|
|
|
|
cmpResult == APFloat::cmpGreaterThan ||
|
|
|
|
cmpResult == APFloat::cmpEqual;
|
|
|
|
case CmpFPredicate::ULT:
|
|
|
|
return cmpResult == APFloat::cmpUnordered ||
|
|
|
|
cmpResult == APFloat::cmpLessThan;
|
|
|
|
case CmpFPredicate::ULE:
|
|
|
|
return cmpResult == APFloat::cmpUnordered ||
|
|
|
|
cmpResult == APFloat::cmpLessThan || cmpResult == APFloat::cmpEqual;
|
|
|
|
case CmpFPredicate::UNE:
|
|
|
|
return cmpResult != APFloat::cmpEqual;
|
|
|
|
case CmpFPredicate::UNO:
|
|
|
|
return cmpResult == APFloat::cmpUnordered;
|
|
|
|
case CmpFPredicate::TRUE:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
llvm_unreachable("unknown comparison predicate");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Constant folding hook for comparisons.
|
2019-05-16 12:51:45 -07:00
|
|
|
OpFoldResult CmpFOp::fold(ArrayRef<Attribute> operands) {
|
2019-05-06 17:51:08 -07:00
|
|
|
assert(operands.size() == 2 && "cmpf takes two arguments");
|
|
|
|
|
|
|
|
auto lhs = operands.front().dyn_cast_or_null<FloatAttr>();
|
|
|
|
auto rhs = operands.back().dyn_cast_or_null<FloatAttr>();
|
|
|
|
if (!lhs || !rhs ||
|
|
|
|
// TODO(b/122019992) Implement and test constant folding for nan/inf when
|
|
|
|
// it is possible to have constant nan/inf
|
|
|
|
!lhs.getValue().isFinite() || !rhs.getValue().isFinite())
|
|
|
|
return {};
|
|
|
|
|
|
|
|
auto val = applyCmpPredicate(getPredicate(), lhs.getValue(), rhs.getValue());
|
2019-05-16 12:51:45 -07:00
|
|
|
return IntegerAttr::get(IntegerType::get(1, getContext()), APInt(1, val));
|
2019-05-06 17:51:08 -07:00
|
|
|
}
|
|
|
|
|
2019-03-01 16:58:00 -08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// CondBranchOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
/// cond_br true, ^bb1, ^bb2 -> br ^bb1
|
|
|
|
/// cond_br false, ^bb1, ^bb2 -> br ^bb2
|
|
|
|
///
|
|
|
|
struct SimplifyConstCondBranchPred : public RewritePattern {
|
|
|
|
SimplifyConstCondBranchPred(MLIRContext *context)
|
|
|
|
: RewritePattern(CondBranchOp::getOperationName(), 1, context) {}
|
|
|
|
|
2019-03-28 08:24:38 -07:00
|
|
|
PatternMatchResult matchAndRewrite(Operation *op,
|
2019-03-25 11:53:03 -07:00
|
|
|
PatternRewriter &rewriter) const override {
|
2019-05-11 17:57:32 -07:00
|
|
|
auto condbr = cast<CondBranchOp>(op);
|
2019-03-01 16:58:00 -08:00
|
|
|
|
2019-03-25 11:53:03 -07:00
|
|
|
// Check that the condition is a constant.
|
2019-03-25 13:02:06 -07:00
|
|
|
if (!matchPattern(condbr.getCondition(), m_Op<ConstantOp>()))
|
2019-03-25 11:53:03 -07:00
|
|
|
return matchFailure();
|
|
|
|
|
2019-03-01 16:58:00 -08:00
|
|
|
Block *foldedDest;
|
|
|
|
SmallVector<Value *, 4> branchArgs;
|
|
|
|
|
|
|
|
// If the condition is known to evaluate to false we fold to a branch to the
|
|
|
|
// false destination. Otherwise, we fold to a branch to the true
|
|
|
|
// destination.
|
2019-03-25 13:02:06 -07:00
|
|
|
if (matchPattern(condbr.getCondition(), m_Zero())) {
|
|
|
|
foldedDest = condbr.getFalseDest();
|
|
|
|
branchArgs.assign(condbr.false_operand_begin(),
|
|
|
|
condbr.false_operand_end());
|
2019-03-01 16:58:00 -08:00
|
|
|
} else {
|
2019-03-25 13:02:06 -07:00
|
|
|
foldedDest = condbr.getTrueDest();
|
|
|
|
branchArgs.assign(condbr.true_operand_begin(), condbr.true_operand_end());
|
2019-03-01 16:58:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
rewriter.replaceOpWithNewOp<BranchOp>(op, foldedDest, branchArgs);
|
2019-03-25 11:53:03 -07:00
|
|
|
return matchSuccess();
|
2019-03-01 16:58:00 -08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
} // end anonymous namespace.
|
|
|
|
|
|
|
|
void CondBranchOp::build(Builder *builder, OperationState *result,
|
|
|
|
Value *condition, Block *trueDest,
|
|
|
|
ArrayRef<Value *> trueOperands, Block *falseDest,
|
|
|
|
ArrayRef<Value *> falseOperands) {
|
|
|
|
result->addOperands(condition);
|
|
|
|
result->addSuccessor(trueDest, trueOperands);
|
|
|
|
result->addSuccessor(falseDest, falseOperands);
|
|
|
|
}
|
|
|
|
|
2019-05-06 22:01:31 -07:00
|
|
|
ParseResult CondBranchOp::parse(OpAsmParser *parser, OperationState *result) {
|
2019-03-01 16:58:00 -08:00
|
|
|
SmallVector<Value *, 4> destOperands;
|
|
|
|
Block *dest;
|
|
|
|
OpAsmParser::OperandType condInfo;
|
|
|
|
|
|
|
|
// Parse the condition.
|
|
|
|
Type int1Ty = parser->getBuilder().getI1Type();
|
|
|
|
if (parser->parseOperand(condInfo) || parser->parseComma() ||
|
|
|
|
parser->resolveOperand(condInfo, int1Ty, result->operands)) {
|
|
|
|
return parser->emitError(parser->getNameLoc(),
|
|
|
|
"expected condition type was boolean (i1)");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse the true successor.
|
|
|
|
if (parser->parseSuccessorAndUseList(dest, destOperands))
|
2019-05-06 22:01:31 -07:00
|
|
|
return failure();
|
2019-03-01 16:58:00 -08:00
|
|
|
result->addSuccessor(dest, destOperands);
|
|
|
|
|
|
|
|
// Parse the false successor.
|
|
|
|
destOperands.clear();
|
|
|
|
if (parser->parseComma() ||
|
|
|
|
parser->parseSuccessorAndUseList(dest, destOperands))
|
2019-05-06 22:01:31 -07:00
|
|
|
return failure();
|
2019-03-01 16:58:00 -08:00
|
|
|
result->addSuccessor(dest, destOperands);
|
|
|
|
|
2019-05-06 22:01:31 -07:00
|
|
|
return success();
|
2019-03-01 16:58:00 -08:00
|
|
|
}
|
|
|
|
|
2019-03-23 09:03:07 -07:00
|
|
|
void CondBranchOp::print(OpAsmPrinter *p) {
|
2019-03-01 16:58:00 -08:00
|
|
|
*p << "cond_br ";
|
|
|
|
p->printOperand(getCondition());
|
|
|
|
*p << ", ";
|
2019-03-26 17:05:09 -07:00
|
|
|
p->printSuccessorAndUseList(getOperation(), trueIndex);
|
2019-03-01 16:58:00 -08:00
|
|
|
*p << ", ";
|
2019-03-26 17:05:09 -07:00
|
|
|
p->printSuccessorAndUseList(getOperation(), falseIndex);
|
2019-03-01 16:58:00 -08:00
|
|
|
}
|
|
|
|
|
2019-04-02 13:09:34 -07:00
|
|
|
LogicalResult CondBranchOp::verify() {
|
2019-03-01 16:58:00 -08:00
|
|
|
if (!getCondition()->getType().isInteger(1))
|
|
|
|
return emitOpError("expected condition type was boolean (i1)");
|
2019-04-02 13:09:34 -07:00
|
|
|
return success();
|
2019-03-01 16:58:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void CondBranchOp::getCanonicalizationPatterns(
|
|
|
|
OwningRewritePatternList &results, MLIRContext *context) {
|
2019-03-19 08:45:06 -07:00
|
|
|
results.push_back(llvm::make_unique<SimplifyConstCondBranchPred>(context));
|
2019-03-01 16:58:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
Block *CondBranchOp::getTrueDest() {
|
2019-03-26 17:05:09 -07:00
|
|
|
return getOperation()->getSuccessor(trueIndex);
|
2019-03-01 16:58:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
Block *CondBranchOp::getFalseDest() {
|
2019-03-26 17:05:09 -07:00
|
|
|
return getOperation()->getSuccessor(falseIndex);
|
2019-03-01 16:58:00 -08:00
|
|
|
}
|
|
|
|
|
2019-03-24 13:02:43 -07:00
|
|
|
unsigned CondBranchOp::getNumTrueOperands() {
|
2019-03-26 17:05:09 -07:00
|
|
|
return getOperation()->getNumSuccessorOperands(trueIndex);
|
2019-03-01 16:58:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void CondBranchOp::eraseTrueOperand(unsigned index) {
|
2019-03-26 17:05:09 -07:00
|
|
|
getOperation()->eraseSuccessorOperand(trueIndex, index);
|
2019-03-01 16:58:00 -08:00
|
|
|
}
|
|
|
|
|
2019-03-24 13:02:43 -07:00
|
|
|
unsigned CondBranchOp::getNumFalseOperands() {
|
2019-03-26 17:05:09 -07:00
|
|
|
return getOperation()->getNumSuccessorOperands(falseIndex);
|
2019-03-01 16:58:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void CondBranchOp::eraseFalseOperand(unsigned index) {
|
2019-03-26 17:05:09 -07:00
|
|
|
getOperation()->eraseSuccessorOperand(falseIndex, index);
|
2019-03-01 16:58:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Constant*Op
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2019-05-14 08:27:32 -07:00
|
|
|
static void print(OpAsmPrinter *p, ConstantOp &op) {
|
2019-03-01 16:58:00 -08:00
|
|
|
*p << "constant ";
|
2019-05-02 11:52:33 -07:00
|
|
|
p->printOptionalAttrDict(op.getAttrs(), /*elidedAttrs=*/{"value"});
|
2019-03-01 16:58:00 -08:00
|
|
|
|
2019-05-02 11:52:33 -07:00
|
|
|
if (op.getAttrs().size() > 1)
|
2019-03-01 16:58:00 -08:00
|
|
|
*p << ' ';
|
2019-05-08 14:46:39 -07:00
|
|
|
p->printAttributeAndType(op.getValue());
|
2019-05-22 13:41:23 -07:00
|
|
|
|
|
|
|
// If the value is a function, print a trailing type.
|
|
|
|
if (op.getValue().isa<FunctionAttr>()) {
|
|
|
|
*p << " : ";
|
|
|
|
p->printType(op.getType());
|
|
|
|
}
|
2019-03-01 16:58:00 -08:00
|
|
|
}
|
|
|
|
|
2019-05-06 22:01:31 -07:00
|
|
|
static ParseResult parseConstantOp(OpAsmParser *parser,
|
|
|
|
OperationState *result) {
|
2019-03-01 16:58:00 -08:00
|
|
|
Attribute valueAttr;
|
|
|
|
if (parser->parseOptionalAttributeDict(result->attributes) ||
|
|
|
|
parser->parseAttribute(valueAttr, "value", result->attributes))
|
2019-05-06 22:01:31 -07:00
|
|
|
return failure();
|
2019-03-01 16:58:00 -08:00
|
|
|
|
2019-05-22 13:41:23 -07:00
|
|
|
// If the attribute is a function, then we expect a trailing type.
|
|
|
|
Type type;
|
|
|
|
if (!valueAttr.isa<FunctionAttr>())
|
|
|
|
type = valueAttr.getType();
|
|
|
|
else if (parser->parseColonType(type))
|
|
|
|
return failure();
|
|
|
|
|
2019-05-08 14:46:39 -07:00
|
|
|
// Add the attribute type to the list.
|
2019-05-22 13:41:23 -07:00
|
|
|
return parser->addTypeToList(type, result->types);
|
2019-03-01 16:58:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// The constant op requires an attribute, and furthermore requires that it
|
|
|
|
/// matches the return type.
|
2019-05-02 11:52:33 -07:00
|
|
|
static LogicalResult verify(ConstantOp &op) {
|
|
|
|
auto value = op.getValue();
|
2019-03-01 16:58:00 -08:00
|
|
|
if (!value)
|
2019-05-02 11:52:33 -07:00
|
|
|
return op.emitOpError("requires a 'value' attribute");
|
2019-03-01 16:58:00 -08:00
|
|
|
|
2019-05-02 11:52:33 -07:00
|
|
|
auto type = op.getType();
|
2019-05-22 13:41:23 -07:00
|
|
|
if (!value.getType().isa<NoneType>() && type != value.getType())
|
2019-05-08 12:11:10 -07:00
|
|
|
return op.emitOpError() << "requires attribute's type (" << value.getType()
|
|
|
|
<< ") to match op's return type (" << type << ")";
|
2019-03-01 16:58:00 -08:00
|
|
|
|
2019-05-08 12:11:10 -07:00
|
|
|
if (type.isa<IndexType>())
|
|
|
|
return success();
|
|
|
|
|
|
|
|
if (auto intAttr = value.dyn_cast<IntegerAttr>()) {
|
2019-03-01 16:58:00 -08:00
|
|
|
// If the type has a known bitwidth we verify that the value can be
|
|
|
|
// represented with the given bitwidth.
|
2019-05-08 12:11:10 -07:00
|
|
|
auto bitwidth = type.cast<IntegerType>().getWidth();
|
|
|
|
auto intVal = intAttr.getValue();
|
|
|
|
if (!intVal.isSignedIntN(bitwidth) && !intVal.isIntN(bitwidth))
|
|
|
|
return op.emitOpError("requires 'value' to be an integer within the "
|
|
|
|
"range of the integer result type");
|
2019-04-02 13:09:34 -07:00
|
|
|
return success();
|
2019-03-01 16:58:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (type.isa<FloatType>()) {
|
|
|
|
if (!value.isa<FloatAttr>())
|
2019-05-02 11:52:33 -07:00
|
|
|
return op.emitOpError("requires 'value' to be a floating point constant");
|
2019-04-02 13:09:34 -07:00
|
|
|
return success();
|
2019-03-01 16:58:00 -08:00
|
|
|
}
|
|
|
|
|
2019-05-16 00:12:45 -07:00
|
|
|
if (type.isa<ShapedType>()) {
|
2019-03-01 16:58:00 -08:00
|
|
|
if (!value.isa<ElementsAttr>())
|
2019-05-16 00:12:45 -07:00
|
|
|
return op.emitOpError("requires 'value' to be a shaped constant");
|
2019-04-02 13:09:34 -07:00
|
|
|
return success();
|
2019-03-01 16:58:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (type.isa<FunctionType>()) {
|
2019-05-22 13:41:23 -07:00
|
|
|
auto fnAttr = value.dyn_cast<FunctionAttr>();
|
|
|
|
if (!fnAttr)
|
2019-05-02 11:52:33 -07:00
|
|
|
return op.emitOpError("requires 'value' to be a function reference");
|
2019-05-22 13:41:23 -07:00
|
|
|
|
|
|
|
// Try to find the referenced function.
|
|
|
|
auto *fn = op.getOperation()->getFunction()->getModule()->getNamedFunction(
|
|
|
|
fnAttr.getValue());
|
|
|
|
if (!fn)
|
|
|
|
return op.emitOpError("reference to undefined function 'bar'");
|
|
|
|
|
|
|
|
// Check that the referenced function has the correct type.
|
|
|
|
if (fn->getType() != type)
|
|
|
|
return op.emitOpError("reference to function with mismatched type");
|
|
|
|
|
2019-04-02 13:09:34 -07:00
|
|
|
return success();
|
2019-03-01 16:58:00 -08:00
|
|
|
}
|
|
|
|
|
2019-05-02 11:52:33 -07:00
|
|
|
return op.emitOpError(
|
2019-03-01 16:58:00 -08:00
|
|
|
"requires a result type that aligns with the 'value' attribute");
|
|
|
|
}
|
|
|
|
|
2019-05-16 12:51:45 -07:00
|
|
|
OpFoldResult ConstantOp::fold(ArrayRef<Attribute> operands) {
|
2019-03-01 16:58:00 -08:00
|
|
|
assert(operands.empty() && "constant has no operands");
|
|
|
|
return getValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConstantFloatOp::build(Builder *builder, OperationState *result,
|
|
|
|
const APFloat &value, FloatType type) {
|
|
|
|
ConstantOp::build(builder, result, type, builder->getFloatAttr(type, value));
|
|
|
|
}
|
|
|
|
|
2019-05-11 12:45:35 -07:00
|
|
|
bool ConstantFloatOp::classof(Operation *op) {
|
|
|
|
return ConstantOp::classof(op) &&
|
2019-03-01 16:58:00 -08:00
|
|
|
op->getResult(0)->getType().isa<FloatType>();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// ConstantIntOp only matches values whose result type is an IntegerType.
|
2019-05-11 12:45:35 -07:00
|
|
|
bool ConstantIntOp::classof(Operation *op) {
|
|
|
|
return ConstantOp::classof(op) &&
|
2019-03-01 16:58:00 -08:00
|
|
|
op->getResult(0)->getType().isa<IntegerType>();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConstantIntOp::build(Builder *builder, OperationState *result,
|
|
|
|
int64_t value, unsigned width) {
|
|
|
|
Type type = builder->getIntegerType(width);
|
|
|
|
ConstantOp::build(builder, result, type,
|
|
|
|
builder->getIntegerAttr(type, value));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Build a constant int op producing an integer with the specified type,
|
|
|
|
/// which must be an integer type.
|
|
|
|
void ConstantIntOp::build(Builder *builder, OperationState *result,
|
|
|
|
int64_t value, Type type) {
|
|
|
|
assert(type.isa<IntegerType>() && "ConstantIntOp can only have integer type");
|
|
|
|
ConstantOp::build(builder, result, type,
|
|
|
|
builder->getIntegerAttr(type, value));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// ConstantIndexOp only matches values whose result type is Index.
|
2019-05-11 12:45:35 -07:00
|
|
|
bool ConstantIndexOp::classof(Operation *op) {
|
|
|
|
return ConstantOp::classof(op) && op->getResult(0)->getType().isIndex();
|
2019-03-01 16:58:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void ConstantIndexOp::build(Builder *builder, OperationState *result,
|
|
|
|
int64_t value) {
|
|
|
|
Type type = builder->getIndexType();
|
|
|
|
ConstantOp::build(builder, result, type,
|
|
|
|
builder->getIntegerAttr(type, value));
|
|
|
|
}
|
|
|
|
|
2018-08-15 15:39:26 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// DeallocOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
2019-01-16 12:39:03 -08:00
|
|
|
namespace {
|
2019-03-28 08:24:38 -07:00
|
|
|
/// Fold Dealloc operations that are deallocating an AllocOp that is only used
|
2019-01-16 12:39:03 -08:00
|
|
|
/// by other Dealloc operations.
|
|
|
|
struct SimplifyDeadDealloc : public RewritePattern {
|
|
|
|
SimplifyDeadDealloc(MLIRContext *context)
|
|
|
|
: RewritePattern(DeallocOp::getOperationName(), 1, context) {}
|
|
|
|
|
2019-03-28 08:24:38 -07:00
|
|
|
PatternMatchResult matchAndRewrite(Operation *op,
|
2019-03-25 11:53:03 -07:00
|
|
|
PatternRewriter &rewriter) const override {
|
2019-05-11 17:57:32 -07:00
|
|
|
auto dealloc = cast<DeallocOp>(op);
|
2019-01-16 12:39:03 -08:00
|
|
|
|
2019-03-28 08:24:38 -07:00
|
|
|
// Check that the memref operand's defining operation is an AllocOp.
|
2019-05-08 22:38:01 -07:00
|
|
|
Value *memref = dealloc.memref();
|
2019-03-28 08:24:38 -07:00
|
|
|
Operation *defOp = memref->getDefiningOp();
|
2019-04-23 14:38:26 -07:00
|
|
|
if (!isa_and_nonnull<AllocOp>(defOp))
|
2019-01-16 12:39:03 -08:00
|
|
|
return matchFailure();
|
|
|
|
|
|
|
|
// Check that all of the uses of the AllocOp are other DeallocOps.
|
2019-05-18 11:09:07 -07:00
|
|
|
for (auto *user : memref->getUsers())
|
|
|
|
if (!isa<DeallocOp>(user))
|
2019-01-16 12:39:03 -08:00
|
|
|
return matchFailure();
|
|
|
|
|
|
|
|
// Erase the dealloc operation.
|
|
|
|
op->erase();
|
2019-03-25 11:53:03 -07:00
|
|
|
return matchSuccess();
|
2019-01-16 12:39:03 -08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
} // end anonymous namespace.
|
2018-08-15 15:39:26 -07:00
|
|
|
|
2019-05-14 08:27:32 -07:00
|
|
|
static void print(OpAsmPrinter *p, DeallocOp op) {
|
2019-05-08 22:38:01 -07:00
|
|
|
*p << "dealloc " << *op.memref() << " : " << op.memref()->getType();
|
2018-08-15 15:39:26 -07:00
|
|
|
}
|
|
|
|
|
2019-05-08 22:38:01 -07:00
|
|
|
static ParseResult parseDeallocOp(OpAsmParser *parser, OperationState *result) {
|
2018-08-15 15:39:26 -07:00
|
|
|
OpAsmParser::OperandType memrefInfo;
|
2018-10-30 14:59:22 -07:00
|
|
|
MemRefType type;
|
2018-08-15 15:39:26 -07:00
|
|
|
|
2019-05-06 22:01:31 -07:00
|
|
|
return failure(parser->parseOperand(memrefInfo) ||
|
|
|
|
parser->parseColonType(type) ||
|
|
|
|
parser->resolveOperand(memrefInfo, type, result->operands));
|
2018-08-15 15:39:26 -07:00
|
|
|
}
|
|
|
|
|
2019-05-08 22:38:01 -07:00
|
|
|
static LogicalResult verify(DeallocOp op) {
|
|
|
|
if (!op.memref()->getType().isa<MemRefType>())
|
|
|
|
return op.emitOpError("operand must be a memref");
|
2019-04-02 13:09:34 -07:00
|
|
|
return success();
|
2018-08-15 15:39:26 -07:00
|
|
|
}
|
|
|
|
|
2018-11-28 15:09:39 -08:00
|
|
|
void DeallocOp::getCanonicalizationPatterns(OwningRewritePatternList &results,
|
2018-10-25 16:44:04 -07:00
|
|
|
MLIRContext *context) {
|
|
|
|
/// dealloc(memrefcast) -> dealloc
|
|
|
|
results.push_back(
|
2019-03-19 08:45:06 -07:00
|
|
|
llvm::make_unique<MemRefCastFolder>(getOperationName(), context));
|
|
|
|
results.push_back(llvm::make_unique<SimplifyDeadDealloc>(context));
|
2018-10-25 16:44:04 -07:00
|
|
|
}
|
|
|
|
|
2018-08-09 12:28:58 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// DimOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2019-05-14 08:27:32 -07:00
|
|
|
static void print(OpAsmPrinter *p, DimOp op) {
|
2019-05-10 15:26:23 -07:00
|
|
|
*p << "dim " << *op.getOperand() << ", " << op.getIndex();
|
|
|
|
p->printOptionalAttrDict(op.getAttrs(), /*elidedAttrs=*/{"index"});
|
|
|
|
*p << " : " << op.getOperand()->getType();
|
2018-07-05 09:12:11 -07:00
|
|
|
}
|
|
|
|
|
2019-05-10 15:26:23 -07:00
|
|
|
static ParseResult parseDimOp(OpAsmParser *parser, OperationState *result) {
|
2018-07-25 11:15:20 -07:00
|
|
|
OpAsmParser::OperandType operandInfo;
|
2018-10-25 15:46:10 -07:00
|
|
|
IntegerAttr indexAttr;
|
2018-10-30 14:59:22 -07:00
|
|
|
Type type;
|
2018-11-15 17:53:51 -08:00
|
|
|
Type indexType = parser->getBuilder().getIndexType();
|
2018-08-02 16:54:36 -07:00
|
|
|
|
2019-05-06 22:01:31 -07:00
|
|
|
return failure(parser->parseOperand(operandInfo) || parser->parseComma() ||
|
|
|
|
parser->parseAttribute(indexAttr, indexType, "index",
|
|
|
|
result->attributes) ||
|
|
|
|
parser->parseOptionalAttributeDict(result->attributes) ||
|
|
|
|
parser->parseColonType(type) ||
|
|
|
|
parser->resolveOperand(operandInfo, type, result->operands) ||
|
|
|
|
parser->addTypeToList(indexType, result->types));
|
2018-07-25 11:15:20 -07:00
|
|
|
}
|
|
|
|
|
2019-05-10 15:26:23 -07:00
|
|
|
static LogicalResult verify(DimOp op) {
|
2018-07-06 10:46:19 -07:00
|
|
|
// Check that we have an integer index operand.
|
2019-05-10 15:26:23 -07:00
|
|
|
auto indexAttr = op.getAttrOfType<IntegerAttr>("index");
|
2018-07-06 10:46:19 -07:00
|
|
|
if (!indexAttr)
|
2019-05-10 15:26:23 -07:00
|
|
|
return op.emitOpError("requires an integer attribute named 'index'");
|
2018-11-12 06:33:22 -08:00
|
|
|
uint64_t index = indexAttr.getValue().getZExtValue();
|
2018-07-24 08:34:58 -07:00
|
|
|
|
2019-05-10 15:26:23 -07:00
|
|
|
auto type = op.getOperand()->getType();
|
2018-10-30 14:59:22 -07:00
|
|
|
if (auto tensorType = type.dyn_cast<RankedTensorType>()) {
|
2019-05-03 19:48:57 -07:00
|
|
|
if (index >= static_cast<uint64_t>(tensorType.getRank()))
|
2019-05-10 15:26:23 -07:00
|
|
|
return op.emitOpError("index is out of range");
|
2018-10-30 14:59:22 -07:00
|
|
|
} else if (auto memrefType = type.dyn_cast<MemRefType>()) {
|
|
|
|
if (index >= memrefType.getRank())
|
2019-05-10 15:26:23 -07:00
|
|
|
return op.emitOpError("index is out of range");
|
2018-07-06 10:46:19 -07:00
|
|
|
|
2018-10-30 14:59:22 -07:00
|
|
|
} else if (type.isa<UnrankedTensorType>()) {
|
2018-07-24 08:34:58 -07:00
|
|
|
// ok, assumed to be in-range.
|
|
|
|
} else {
|
2019-05-10 15:26:23 -07:00
|
|
|
return op.emitOpError("requires an operand with tensor or memref type");
|
2018-07-24 08:34:58 -07:00
|
|
|
}
|
2018-07-06 10:46:19 -07:00
|
|
|
|
2019-04-02 13:09:34 -07:00
|
|
|
return success();
|
2018-07-06 10:46:19 -07:00
|
|
|
}
|
|
|
|
|
2019-05-16 12:51:45 -07:00
|
|
|
OpFoldResult DimOp::fold(ArrayRef<Attribute> operands) {
|
2018-10-05 18:24:18 -07:00
|
|
|
// Constant fold dim when the size along the index referred to is a constant.
|
2018-10-30 14:59:22 -07:00
|
|
|
auto opType = getOperand()->getType();
|
2019-01-23 14:39:45 -08:00
|
|
|
int64_t indexSize = -1;
|
2019-05-10 15:26:23 -07:00
|
|
|
if (auto tensorType = opType.dyn_cast<RankedTensorType>())
|
2018-10-30 14:59:22 -07:00
|
|
|
indexSize = tensorType.getShape()[getIndex()];
|
2019-05-10 15:26:23 -07:00
|
|
|
else if (auto memrefType = opType.dyn_cast<MemRefType>())
|
2018-10-30 14:59:22 -07:00
|
|
|
indexSize = memrefType.getShape()[getIndex()];
|
2018-10-05 09:28:49 -07:00
|
|
|
|
|
|
|
if (indexSize >= 0)
|
2019-05-16 12:51:45 -07:00
|
|
|
return IntegerAttr::get(IndexType::get(getContext()), indexSize);
|
2018-10-05 09:28:49 -07:00
|
|
|
|
2019-05-16 12:51:45 -07:00
|
|
|
return {};
|
2018-10-05 09:28:49 -07:00
|
|
|
}
|
|
|
|
|
2019-01-06 14:08:42 -08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// DivISOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2019-05-16 12:51:45 -07:00
|
|
|
OpFoldResult DivISOp::fold(ArrayRef<Attribute> operands) {
|
2019-01-06 14:08:42 -08:00
|
|
|
assert(operands.size() == 2 && "binary operation takes two operands");
|
|
|
|
|
|
|
|
auto lhs = operands.front().dyn_cast_or_null<IntegerAttr>();
|
|
|
|
auto rhs = operands.back().dyn_cast_or_null<IntegerAttr>();
|
|
|
|
if (!lhs || !rhs)
|
|
|
|
return {};
|
|
|
|
|
|
|
|
// Don't fold if it requires division by zero.
|
2019-05-16 12:51:45 -07:00
|
|
|
if (rhs.getValue().isNullValue())
|
2019-01-06 14:08:42 -08:00
|
|
|
return {};
|
|
|
|
|
|
|
|
// Don't fold if it would overflow.
|
|
|
|
bool overflow;
|
|
|
|
auto result = lhs.getValue().sdiv_ov(rhs.getValue(), overflow);
|
|
|
|
return overflow ? IntegerAttr{} : IntegerAttr::get(lhs.getType(), result);
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// DivIUOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2019-05-16 12:51:45 -07:00
|
|
|
OpFoldResult DivIUOp::fold(ArrayRef<Attribute> operands) {
|
2019-01-06 14:08:42 -08:00
|
|
|
assert(operands.size() == 2 && "binary operation takes two operands");
|
|
|
|
|
|
|
|
auto lhs = operands.front().dyn_cast_or_null<IntegerAttr>();
|
|
|
|
auto rhs = operands.back().dyn_cast_or_null<IntegerAttr>();
|
|
|
|
if (!lhs || !rhs)
|
|
|
|
return {};
|
|
|
|
|
|
|
|
// Don't fold if it requires division by zero.
|
|
|
|
if (rhs.getValue().isNullValue()) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
return IntegerAttr::get(lhs.getType(), lhs.getValue().udiv(rhs.getValue()));
|
|
|
|
}
|
|
|
|
|
2018-10-09 15:04:27 -07:00
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// DmaStartOp
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
2018-11-08 17:31:01 -08:00
|
|
|
void DmaStartOp::build(Builder *builder, OperationState *result,
|
2018-12-27 14:35:10 -08:00
|
|
|
Value *srcMemRef, ArrayRef<Value *> srcIndices,
|
|
|
|
Value *destMemRef, ArrayRef<Value *> destIndices,
|
|
|
|
Value *numElements, Value *tagMemRef,
|
|
|
|
ArrayRef<Value *> tagIndices, Value *stride,
|
|
|
|
Value *elementsPerStride) {
|
2018-11-08 17:31:01 -08:00
|
|
|
result->addOperands(srcMemRef);
|
|
|
|
result->addOperands(srcIndices);
|
|
|
|
result->addOperands(destMemRef);
|
|
|
|
result->addOperands(destIndices);
|
|
|
|
result->addOperands(numElements);
|
|
|
|
result->addOperands(tagMemRef);
|
|
|
|
result->addOperands(tagIndices);
|
2018-12-05 15:30:25 -08:00
|
|
|
if (stride) {
|
|
|
|
result->addOperands(stride);
|
|
|
|
result->addOperands(elementsPerStride);
|
|
|
|
}
|
2018-11-08 17:31:01 -08:00
|
|
|
}
|
|
|
|
|
2019-03-23 09:03:07 -07:00
|
|
|
void DmaStartOp::print(OpAsmPrinter *p) {
|
2019-03-02 18:03:03 -08:00
|
|
|
*p << "dma_start " << *getSrcMemRef() << '[';
|
2018-10-09 15:04:27 -07:00
|
|
|
p->printOperands(getSrcIndices());
|
|
|
|
*p << "], " << *getDstMemRef() << '[';
|
|
|
|
p->printOperands(getDstIndices());
|
|
|
|
*p << "], " << *getNumElements();
|
|
|
|
*p << ", " << *getTagMemRef() << '[';
|
|
|
|
p->printOperands(getTagIndices());
|
|
|
|
*p << ']';
|
2018-12-05 15:30:25 -08:00
|
|
|
if (isStrided()) {
|
|
|
|
*p << ", " << *getStride();
|
|
|
|
*p << ", " << *getNumElementsPerStride();
|
|
|
|
}
|
2018-10-09 15:04:27 -07:00
|
|
|
p->printOptionalAttrDict(getAttrs());
|
2018-10-30 14:59:22 -07:00
|
|
|
*p << " : " << getSrcMemRef()->getType();
|
|
|
|
*p << ", " << getDstMemRef()->getType();
|
|
|
|
*p << ", " << getTagMemRef()->getType();
|
2018-10-09 15:04:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Parse DmaStartOp.
|
2018-10-18 11:14:26 -07:00
|
|
|
// Ex:
|
2018-10-09 15:04:27 -07:00
|
|
|
// %dma_id = dma_start %src[%i, %j], %dst[%k, %l], %size,
|
2019-02-19 10:26:53 -08:00
|
|
|
// %tag[%index], %stride, %num_elt_per_stride :
|
|
|
|
// : memref<3076 x f32, 0>,
|
|
|
|
// memref<1024 x f32, 2>,
|
|
|
|
// memref<1 x i32>
|
2018-10-09 15:04:27 -07:00
|
|
|
//
|
2019-05-06 22:01:31 -07:00
|
|
|
ParseResult DmaStartOp::parse(OpAsmParser *parser, OperationState *result) {
|
2018-10-09 15:04:27 -07:00
|
|
|
OpAsmParser::OperandType srcMemRefInfo;
|
|
|
|
SmallVector<OpAsmParser::OperandType, 4> srcIndexInfos;
|
|
|
|
OpAsmParser::OperandType dstMemRefInfo;
|
|
|
|
SmallVector<OpAsmParser::OperandType, 4> dstIndexInfos;
|
|
|
|
OpAsmParser::OperandType numElementsInfo;
|
|
|
|
OpAsmParser::OperandType tagMemrefInfo;
|
|
|
|
SmallVector<OpAsmParser::OperandType, 4> tagIndexInfos;
|
2018-12-05 15:30:25 -08:00
|
|
|
SmallVector<OpAsmParser::OperandType, 2> strideInfo;
|
2018-10-09 15:04:27 -07:00
|
|
|
|
2018-10-30 14:59:22 -07:00
|
|
|
SmallVector<Type, 3> types;
|
|
|
|
auto indexType = parser->getBuilder().getIndexType();
|
2018-10-09 15:04:27 -07:00
|
|
|
|
|
|
|
// Parse and resolve the following list of operands:
|
|
|
|
// *) source memref followed by its indices (in square brackets).
|
|
|
|
// *) destination memref followed by its indices (in square brackets).
|
|
|
|
// *) dma size in KiB.
|
|
|
|
if (parser->parseOperand(srcMemRefInfo) ||
|
|
|
|
parser->parseOperandList(srcIndexInfos, -1,
|
|
|
|
OpAsmParser::Delimiter::Square) ||
|
|
|
|
parser->parseComma() || parser->parseOperand(dstMemRefInfo) ||
|
|
|
|
parser->parseOperandList(dstIndexInfos, -1,
|
|
|
|
OpAsmParser::Delimiter::Square) ||
|
|
|
|
parser->parseComma() || parser->parseOperand(numElementsInfo) ||
|
|
|
|
parser->parseComma() || parser->parseOperand(tagMemrefInfo) ||
|
|
|
|
parser->parseOperandList(tagIndexInfos, -1,
|
2018-12-05 15:30:25 -08:00
|
|
|
OpAsmParser::Delimiter::Square))
|
2019-05-06 22:01:31 -07:00
|
|
|
return failure();
|
2018-12-05 15:30:25 -08:00
|
|
|
|
|
|
|
// Parse optional stride and elements per stride.
|
|
|
|
if (parser->parseTrailingOperandList(strideInfo)) {
|
2019-05-06 22:01:31 -07:00
|
|
|
return failure();
|
2018-12-05 15:30:25 -08:00
|
|
|
}
|
|
|
|
if (!strideInfo.empty() && strideInfo.size() != 2) {
|
|
|
|
return parser->emitError(parser->getNameLoc(),
|
|
|
|
"expected two stride related operands");
|
|
|
|
}
|
|
|
|
bool isStrided = strideInfo.size() == 2;
|
|
|
|
|
|
|
|
if (parser->parseColonTypeList(types))
|
2019-05-06 22:01:31 -07:00
|
|
|
return failure();
|
2018-10-09 15:04:27 -07:00
|
|
|
|
|
|
|
if (types.size() != 3)
|
|
|
|
return parser->emitError(parser->getNameLoc(), "fewer/more types expected");
|
|
|
|
|
|
|
|
if (parser->resolveOperand(srcMemRefInfo, types[0], result->operands) ||
|
|
|
|
parser->resolveOperands(srcIndexInfos, indexType, result->operands) ||
|
|
|
|
parser->resolveOperand(dstMemRefInfo, types[1], result->operands) ||
|
|
|
|
parser->resolveOperands(dstIndexInfos, indexType, result->operands) ||
|
|
|
|
// size should be an index.
|
|
|
|
parser->resolveOperand(numElementsInfo, indexType, result->operands) ||
|
|
|
|
parser->resolveOperand(tagMemrefInfo, types[2], result->operands) ||
|
|
|
|
// tag indices should be index.
|
|
|
|
parser->resolveOperands(tagIndexInfos, indexType, result->operands))
|
2019-05-06 22:01:31 -07:00
|
|
|
return failure();
|
2018-10-09 15:04:27 -07:00
|
|
|
|
2019-01-15 10:27:32 -08:00
|
|
|
if (!types[0].isa<MemRefType>())
|
|
|
|
return parser->emitError(parser->getNameLoc(),
|
|
|
|
"expected source to be of memref type");
|
|
|
|
|
|
|
|
if (!types[1].isa<MemRefType>())
|
|
|
|
return parser->emitError(parser->getNameLoc(),
|
|
|
|
"expected destination to be of memref type");
|
|
|
|
|
|
|
|
if (!types[2].isa<MemRefType>())
|
|
|
|
return parser->emitError(parser->getNameLoc(),
|
|
|
|
"expected tag to be of memref type");
|
|
|
|
|
2018-12-05 15:30:25 -08:00
|
|
|
if (isStrided) {
|
|
|
|
if (parser->resolveOperand(strideInfo[0], indexType, result->operands) ||
|
|
|
|
parser->resolveOperand(strideInfo[1], indexType, result->operands))
|
2019-05-06 22:01:31 -07:00
|
|
|
return failure();
|
2018-12-05 15:30:25 -08:00
|
|
|
}
|
|
|
|
|
2018-10-09 15:04:27 -07:00
|
|
|
// Check that source/destination index list size matches associated rank.
|
2018-10-30 14:59:22 -07:00
|
|
|
if (srcIndexInfos.size() != types[0].cast<MemRefType>().getRank() ||
|
|
|
|
dstIndexInfos.size() != types[1].cast<MemRefType>().getRank())
|
2018-10-09 15:04:27 -07:00
|
|
|
return parser->emitError(parser->getNameLoc(),
|
|
|
|
"memref rank not equal to indices count");
|
|
|
|
|
2018-10-30 14:59:22 -07:00
|
|
|
if (tagIndexInfos.size() != types[2].cast<MemRefType>().getRank())
|
2018-10-09 15:04:27 -07:00
|
|
|
return parser->emitError(parser->getNameLoc(),
|
|
|
|
"tag memref rank not equal to indices count");
|
|
|
|
|
2019-05-06 22:01:31 -07:00
|
|
|
return success();
|
2018-10-09 15:04:27 -07:00
|
|
|
}
|
|
|
|
|
2019-04-02 13:09:34 -07:00
|
|
|
LogicalResult DmaStartOp::verify() {
|
2018-12-05 15:30:25 -08:00
|
|
|
// DMAs from different memory spaces supported.
|
|
|
|
if (getSrcMemorySpace() == getDstMemorySpace()) {
|
|
|
|
return emitOpError("DMA should be between different memory spaces");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (getNumOperands() != getTagMemRefRank() + getSrcMemRefRank() +
|
|
|
|
getDstMemRefRank() + 3 + 1 &&
|
|
|
|
getNumOperands() != getTagMemRefRank() + getSrcMemRefRank() +
|
|
|
|
getDstMemRefRank() + 3 + 1 + 2) {
|
|
|
|
return emitOpError("incorrect number of operands");
|
|
|
|
}
|
2019-04-02 13:09:34 -07:00
|
|
|
return success();
|
2018-12-05 15:30:25 -08:00
|
|
|
}
|
|
|
|
|
2018-11-28 15:09:39 -08:00
|
|
|
void DmaStartOp::getCanonicalizationPatterns(OwningRewritePatternList &results,
|
2018-10-25 16:44:04 -07:00
|
|
|
MLIRContext *context) {
|
|
|
|
/// dma_start(memrefcast) -> dma_start
|
|
|
|
results.push_back(
|
2019-03-19 08:45:06 -07:00
|
|
|
llvm::make_unique<MemRefCastFolder>(getOperationName(), context));
|
2018-10-25 16:44:04 -07:00
|
|
|
}
|
|
|
|
|
2018-10-09 15:04:27 -07:00
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// DmaWaitOp
|
|
|
|
// ---------------------------------------------------------------------------
|
2018-10-18 11:14:26 -07:00
|
|
|
|
2018-11-08 17:31:01 -08:00
|
|
|
void DmaWaitOp::build(Builder *builder, OperationState *result,
|
2018-12-27 14:35:10 -08:00
|
|
|
Value *tagMemRef, ArrayRef<Value *> tagIndices,
|
|
|
|
Value *numElements) {
|
2018-11-08 17:31:01 -08:00
|
|
|
result->addOperands(tagMemRef);
|
|
|
|
result->addOperands(tagIndices);
|
|
|
|
result->addOperands(numElements);
|
|
|
|
}
|
|
|
|
|
2019-03-23 09:03:07 -07:00
|
|
|
void DmaWaitOp::print(OpAsmPrinter *p) {
|
2019-03-02 18:03:03 -08:00
|
|
|
*p << "dma_wait ";
|
2018-10-09 15:04:27 -07:00
|
|
|
// Print operands.
|
|
|
|
p->printOperand(getTagMemRef());
|
|
|
|
*p << '[';
|
|
|
|
p->printOperands(getTagIndices());
|
2018-10-18 11:14:26 -07:00
|
|
|
*p << "], ";
|
|
|
|
p->printOperand(getNumElements());
|
2018-11-16 20:12:06 -08:00
|
|
|
p->printOptionalAttrDict(getAttrs());
|
2018-12-10 15:17:25 -08:00
|
|
|
*p << " : " << getTagMemRef()->getType();
|
2018-10-09 15:04:27 -07:00
|
|
|
}
|
|
|
|
|
2018-10-18 11:14:26 -07:00
|
|
|
// Parse DmaWaitOp.
|
|
|
|
// Eg:
|
|
|
|
// dma_wait %tag[%index], %num_elements : memref<1 x i32, (d0) -> (d0), 4>
|
|
|
|
//
|
2019-05-06 22:01:31 -07:00
|
|
|
ParseResult DmaWaitOp::parse(OpAsmParser *parser, OperationState *result) {
|
2018-10-09 15:04:27 -07:00
|
|
|
OpAsmParser::OperandType tagMemrefInfo;
|
|
|
|
SmallVector<OpAsmParser::OperandType, 2> tagIndexInfos;
|
2018-10-30 14:59:22 -07:00
|
|
|
Type type;
|
|
|
|
auto indexType = parser->getBuilder().getIndexType();
|
2018-10-18 11:14:26 -07:00
|
|
|
OpAsmParser::OperandType numElementsInfo;
|
2018-10-09 15:04:27 -07:00
|
|
|
|
2018-10-18 11:14:26 -07:00
|
|
|
// Parse tag memref, its indices, and dma size.
|
2018-10-09 15:04:27 -07:00
|
|
|
if (parser->parseOperand(tagMemrefInfo) ||
|
|
|
|
parser->parseOperandList(tagIndexInfos, -1,
|
|
|
|
OpAsmParser::Delimiter::Square) ||
|
2018-10-18 11:14:26 -07:00
|
|
|
parser->parseComma() || parser->parseOperand(numElementsInfo) ||
|
2018-10-09 15:04:27 -07:00
|
|
|
parser->parseColonType(type) ||
|
|
|
|
parser->resolveOperand(tagMemrefInfo, type, result->operands) ||
|
2018-10-18 11:14:26 -07:00
|
|
|
parser->resolveOperands(tagIndexInfos, indexType, result->operands) ||
|
|
|
|
parser->resolveOperand(numElementsInfo, indexType, result->operands))
|
2019-05-06 22:01:31 -07:00
|
|
|
return failure();
|
2018-10-09 15:04:27 -07:00
|
|
|
|
2019-01-15 10:04:28 -08:00
|
|
|
if (!type.isa<MemRefType>())
|
|
|
|
return parser->emitError(parser->getNameLoc(),
|
|
|
|
"expected tag to be of memref type");
|
|
|
|
|
2018-10-30 14:59:22 -07:00
|
|
|
if (tagIndexInfos.size() != type.cast<MemRefType>().getRank())
|
2018-10-09 15:04:27 -07:00
|
|
|
return parser->emitError(parser->getNameLoc(),
|
|
|
|
"tag memref rank not equal to indices count");
|
|
|
|
|
2019-05-06 22:01:31 -07:00
|
|
|
return success();
|
2018-10-09 15:04:27 -07:00
|
|
|
}
|
|
|
|
|
2018-11-28 15:09:39 -08:00
|
|
|
void DmaWaitOp::getCanonicalizationPatterns(OwningRewritePatternList &results,
|
2018-10-25 16:44:04 -07:00
|
|
|
MLIRContext *context) {
|
|
|
|
/// dma_wait(memrefcast) -> dma_wait
|
|
|
|
results.push_back(
|
2019-03-19 08:45:06 -07:00
|
|
|
llvm::make_unique<MemRefCastFolder>(getOperationName(), context));
|
2018-10-25 16:44:04 -07:00
|
|
|
}
|
|
|
|
|
2018-08-23 09:58:23 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// ExtractElementOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2019-05-14 08:27:32 -07:00
|
|
|
static void print(OpAsmPrinter *p, ExtractElementOp op) {
|
2019-05-10 15:26:23 -07:00
|
|
|
*p << "extract_element " << *op.getAggregate() << '[';
|
|
|
|
p->printOperands(op.getIndices());
|
2018-08-23 09:58:23 -07:00
|
|
|
*p << ']';
|
2019-05-10 15:26:23 -07:00
|
|
|
p->printOptionalAttrDict(op.getAttrs());
|
|
|
|
*p << " : " << op.getAggregate()->getType();
|
2018-08-23 09:58:23 -07:00
|
|
|
}
|
|
|
|
|
2019-05-10 15:26:23 -07:00
|
|
|
static ParseResult parseExtractElementOp(OpAsmParser *parser,
|
|
|
|
OperationState *result) {
|
2018-08-23 09:58:23 -07:00
|
|
|
OpAsmParser::OperandType aggregateInfo;
|
|
|
|
SmallVector<OpAsmParser::OperandType, 4> indexInfo;
|
2019-05-16 00:12:45 -07:00
|
|
|
ShapedType type;
|
2018-08-23 09:58:23 -07:00
|
|
|
|
2018-10-06 17:21:53 -07:00
|
|
|
auto affineIntTy = parser->getBuilder().getIndexType();
|
2019-05-06 22:01:31 -07:00
|
|
|
return failure(
|
|
|
|
parser->parseOperand(aggregateInfo) ||
|
|
|
|
parser->parseOperandList(indexInfo, -1, OpAsmParser::Delimiter::Square) ||
|
|
|
|
parser->parseOptionalAttributeDict(result->attributes) ||
|
|
|
|
parser->parseColonType(type) ||
|
|
|
|
parser->resolveOperand(aggregateInfo, type, result->operands) ||
|
|
|
|
parser->resolveOperands(indexInfo, affineIntTy, result->operands) ||
|
|
|
|
parser->addTypeToList(type.getElementType(), result->types));
|
2018-08-23 09:58:23 -07:00
|
|
|
}
|
|
|
|
|
2019-05-10 15:26:23 -07:00
|
|
|
static LogicalResult verify(ExtractElementOp op) {
|
2019-05-16 15:18:49 -07:00
|
|
|
auto aggregateType = op.getAggregate()->getType().cast<ShapedType>();
|
2018-08-23 09:58:23 -07:00
|
|
|
|
2019-05-16 15:18:49 -07:00
|
|
|
// This should be possible with tablegen type constraints
|
2019-05-10 15:26:23 -07:00
|
|
|
if (op.getType() != aggregateType.getElementType())
|
|
|
|
return op.emitOpError("result type must match element type of aggregate");
|
2018-08-23 09:58:23 -07:00
|
|
|
|
2019-05-16 15:18:49 -07:00
|
|
|
// TODO(b/132908002) This should be covered by the op specification in
|
|
|
|
// tablegen, but for some reason it's not.
|
2019-05-10 15:26:23 -07:00
|
|
|
for (auto *idx : op.getIndices())
|
2018-10-30 14:59:22 -07:00
|
|
|
if (!idx->getType().isIndex())
|
2019-05-10 15:26:23 -07:00
|
|
|
return op.emitOpError("index to extract_element must have 'index' type");
|
2018-08-23 09:58:23 -07:00
|
|
|
|
|
|
|
// Verify the # indices match if we have a ranked type.
|
2018-10-30 14:59:22 -07:00
|
|
|
auto aggregateRank = aggregateType.getRank();
|
2019-05-10 15:26:23 -07:00
|
|
|
if (aggregateRank != -1 && aggregateRank != op.getNumOperands() - 1)
|
|
|
|
return op.emitOpError("incorrect number of indices for extract_element");
|
2018-08-23 09:58:23 -07:00
|
|
|
|
2019-04-02 13:09:34 -07:00
|
|
|
return success();
|
2018-08-23 09:58:23 -07:00
|
|
|
}
|
|
|
|
|
2019-05-16 12:51:45 -07:00
|
|
|
OpFoldResult ExtractElementOp::fold(ArrayRef<Attribute> operands) {
|
2019-04-30 09:52:38 -07:00
|
|
|
assert(!operands.empty() && "extract_element takes atleast one operand");
|
2019-01-19 20:54:09 -08:00
|
|
|
|
|
|
|
// The aggregate operand must be a known constant.
|
|
|
|
Attribute aggregate = operands.front();
|
|
|
|
if (!aggregate)
|
2019-05-16 12:51:45 -07:00
|
|
|
return {};
|
2019-01-19 20:54:09 -08:00
|
|
|
|
|
|
|
// If this is a splat elements attribute, simply return the value. All of the
|
|
|
|
// elements of a splat attribute are the same.
|
|
|
|
if (auto splatAggregate = aggregate.dyn_cast<SplatElementsAttr>())
|
|
|
|
return splatAggregate.getValue();
|
|
|
|
|
|
|
|
// Otherwise, collect the constant indices into the aggregate.
|
|
|
|
SmallVector<uint64_t, 8> indices;
|
|
|
|
for (Attribute indice : llvm::drop_begin(operands, 1)) {
|
|
|
|
if (!indice || !indice.isa<IntegerAttr>())
|
2019-05-16 12:51:45 -07:00
|
|
|
return {};
|
2019-01-19 20:54:09 -08:00
|
|
|
indices.push_back(indice.cast<IntegerAttr>().getInt());
|
|
|
|
}
|
|
|
|
|
2019-02-27 16:15:16 -08:00
|
|
|
// If this is an elements attribute, query the value at the given indices.
|
|
|
|
if (auto elementsAttr = aggregate.dyn_cast<ElementsAttr>())
|
|
|
|
return elementsAttr.getValue(indices);
|
2019-05-16 12:51:45 -07:00
|
|
|
return {};
|
2019-01-19 20:54:09 -08:00
|
|
|
}
|
|
|
|
|
2018-08-09 12:28:58 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// LoadOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2018-12-27 14:35:10 -08:00
|
|
|
void LoadOp::build(Builder *builder, OperationState *result, Value *memref,
|
|
|
|
ArrayRef<Value *> indices) {
|
2018-10-30 14:59:22 -07:00
|
|
|
auto memrefType = memref->getType().cast<MemRefType>();
|
2018-08-23 09:58:23 -07:00
|
|
|
result->addOperands(memref);
|
|
|
|
result->addOperands(indices);
|
2018-10-30 14:59:22 -07:00
|
|
|
result->types.push_back(memrefType.getElementType());
|
2018-08-23 09:58:23 -07:00
|
|
|
}
|
|
|
|
|
2019-03-23 09:03:07 -07:00
|
|
|
void LoadOp::print(OpAsmPrinter *p) {
|
2018-07-25 11:15:20 -07:00
|
|
|
*p << "load " << *getMemRef() << '[';
|
|
|
|
p->printOperands(getIndices());
|
2018-08-02 16:54:36 -07:00
|
|
|
*p << ']';
|
|
|
|
p->printOptionalAttrDict(getAttrs());
|
2018-10-30 14:59:22 -07:00
|
|
|
*p << " : " << getMemRefType();
|
2018-07-25 11:15:20 -07:00
|
|
|
}
|
|
|
|
|
2019-05-06 22:01:31 -07:00
|
|
|
ParseResult LoadOp::parse(OpAsmParser *parser, OperationState *result) {
|
2018-07-25 11:15:20 -07:00
|
|
|
OpAsmParser::OperandType memrefInfo;
|
|
|
|
SmallVector<OpAsmParser::OperandType, 4> indexInfo;
|
2018-10-30 14:59:22 -07:00
|
|
|
MemRefType type;
|
2018-07-25 11:15:20 -07:00
|
|
|
|
2018-10-06 17:21:53 -07:00
|
|
|
auto affineIntTy = parser->getBuilder().getIndexType();
|
2019-05-06 22:01:31 -07:00
|
|
|
return failure(
|
|
|
|
parser->parseOperand(memrefInfo) ||
|
|
|
|
parser->parseOperandList(indexInfo, -1, OpAsmParser::Delimiter::Square) ||
|
|
|
|
parser->parseOptionalAttributeDict(result->attributes) ||
|
|
|
|
parser->parseColonType(type) ||
|
|
|
|
parser->resolveOperand(memrefInfo, type, result->operands) ||
|
|
|
|
parser->resolveOperands(indexInfo, affineIntTy, result->operands) ||
|
|
|
|
parser->addTypeToList(type.getElementType(), result->types));
|
2018-07-25 11:15:20 -07:00
|
|
|
}
|
|
|
|
|
2019-04-02 13:09:34 -07:00
|
|
|
LogicalResult LoadOp::verify() {
|
2018-07-28 09:36:25 -07:00
|
|
|
if (getNumOperands() == 0)
|
2018-09-09 20:40:23 -07:00
|
|
|
return emitOpError("expected a memref to load from");
|
2018-07-24 10:13:31 -07:00
|
|
|
|
2018-10-30 14:59:22 -07:00
|
|
|
auto memRefType = getMemRef()->getType().dyn_cast<MemRefType>();
|
2018-07-28 09:36:25 -07:00
|
|
|
if (!memRefType)
|
2018-09-09 20:40:23 -07:00
|
|
|
return emitOpError("first operand must be a memref");
|
2018-07-24 10:13:31 -07:00
|
|
|
|
2018-10-30 14:59:22 -07:00
|
|
|
if (getType() != memRefType.getElementType())
|
2018-09-09 20:40:23 -07:00
|
|
|
return emitOpError("result type must match element type of memref");
|
2018-08-23 09:58:23 -07:00
|
|
|
|
2018-10-30 14:59:22 -07:00
|
|
|
if (memRefType.getRank() != getNumOperands() - 1)
|
2018-09-09 20:40:23 -07:00
|
|
|
return emitOpError("incorrect number of indices for load");
|
2018-08-23 09:58:23 -07:00
|
|
|
|
2018-07-28 09:36:25 -07:00
|
|
|
for (auto *idx : getIndices())
|
2018-10-30 14:59:22 -07:00
|
|
|
if (!idx->getType().isIndex())
|
2018-10-06 17:21:53 -07:00
|
|
|
return emitOpError("index to load must have 'index' type");
|
2018-07-24 17:43:56 -07:00
|
|
|
|
2018-07-28 09:36:25 -07:00
|
|
|
// TODO: Verify we have the right number of indices.
|
2018-07-24 17:43:56 -07:00
|
|
|
|
2018-12-28 08:48:09 -08:00
|
|
|
// TODO: in Function verify that the indices are parameters, IV's, or the
|
2019-02-06 11:08:18 -08:00
|
|
|
// result of an affine.apply.
|
2019-04-02 13:09:34 -07:00
|
|
|
return success();
|
2018-07-24 10:13:31 -07:00
|
|
|
}
|
|
|
|
|
2018-11-28 15:09:39 -08:00
|
|
|
void LoadOp::getCanonicalizationPatterns(OwningRewritePatternList &results,
|
2018-10-25 16:44:04 -07:00
|
|
|
MLIRContext *context) {
|
|
|
|
/// load(memrefcast) -> load
|
|
|
|
results.push_back(
|
2019-03-19 08:45:06 -07:00
|
|
|
llvm::make_unique<MemRefCastFolder>(getOperationName(), context));
|
2018-10-25 16:44:04 -07:00
|
|
|
}
|
|
|
|
|
2018-10-22 09:00:03 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// MemRefCastOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2019-04-27 20:55:38 -07:00
|
|
|
bool MemRefCastOp::areCastCompatible(Type a, Type b) {
|
|
|
|
auto aT = a.dyn_cast<MemRefType>();
|
|
|
|
auto bT = b.dyn_cast<MemRefType>();
|
2018-10-22 09:00:03 -07:00
|
|
|
|
2019-04-27 20:55:38 -07:00
|
|
|
if (!aT || !bT)
|
|
|
|
return false;
|
|
|
|
if (aT.getElementType() != bT.getElementType())
|
|
|
|
return false;
|
|
|
|
if (aT.getAffineMaps() != bT.getAffineMaps())
|
|
|
|
return false;
|
|
|
|
if (aT.getMemorySpace() != bT.getMemorySpace())
|
|
|
|
return false;
|
2018-10-22 09:00:03 -07:00
|
|
|
|
2019-04-27 20:55:38 -07:00
|
|
|
// They must have the same rank, and any specified dimensions must match.
|
|
|
|
if (aT.getRank() != bT.getRank())
|
|
|
|
return false;
|
2018-10-22 09:00:03 -07:00
|
|
|
|
2019-04-27 20:55:38 -07:00
|
|
|
for (unsigned i = 0, e = aT.getRank(); i != e; ++i) {
|
|
|
|
int64_t aDim = aT.getDimSize(i), bDim = bT.getDimSize(i);
|
|
|
|
if (aDim != -1 && bDim != -1 && aDim != bDim)
|
|
|
|
return false;
|
|
|
|
}
|
2018-10-22 09:00:03 -07:00
|
|
|
|
2019-04-27 20:55:38 -07:00
|
|
|
return true;
|
|
|
|
}
|
2018-10-22 09:00:03 -07:00
|
|
|
|
2019-05-16 12:51:45 -07:00
|
|
|
OpFoldResult MemRefCastOp::fold(ArrayRef<Attribute> operands) {
|
|
|
|
return impl::foldCastOp(*this);
|
|
|
|
}
|
2018-10-22 09:00:03 -07:00
|
|
|
|
2018-09-26 10:07:16 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// MulFOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2019-05-16 12:51:45 -07:00
|
|
|
OpFoldResult MulFOp::fold(ArrayRef<Attribute> operands) {
|
2019-01-11 09:12:11 -08:00
|
|
|
return constFoldBinaryOp<FloatAttr>(
|
|
|
|
operands, [](APFloat a, APFloat b) { return a * b; });
|
2018-09-26 10:07:16 -07:00
|
|
|
}
|
|
|
|
|
2018-10-03 09:43:13 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// MulIOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2019-05-16 12:51:45 -07:00
|
|
|
OpFoldResult MulIOp::fold(ArrayRef<Attribute> operands) {
|
2019-02-07 08:26:31 -08:00
|
|
|
/// muli(x, 0) -> 0
|
2019-05-16 12:51:45 -07:00
|
|
|
if (matchPattern(rhs(), m_Zero()))
|
|
|
|
return rhs();
|
2019-02-07 08:26:31 -08:00
|
|
|
/// muli(x, 1) -> x
|
2019-05-16 12:51:45 -07:00
|
|
|
if (matchPattern(rhs(), m_One()))
|
2019-02-07 08:26:31 -08:00
|
|
|
return getOperand(0);
|
2019-05-16 12:51:45 -07:00
|
|
|
|
|
|
|
// TODO: Handle the overflow case.
|
|
|
|
return constFoldBinaryOp<IntegerAttr>(operands,
|
|
|
|
[](APInt a, APInt b) { return a * b; });
|
2018-10-26 11:28:06 -07:00
|
|
|
}
|
|
|
|
|
2019-01-06 14:08:42 -08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// RemISOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2019-05-16 12:51:45 -07:00
|
|
|
OpFoldResult RemISOp::fold(ArrayRef<Attribute> operands) {
|
2019-01-06 14:08:42 -08:00
|
|
|
assert(operands.size() == 2 && "remis takes two operands");
|
|
|
|
|
|
|
|
auto rhs = operands.back().dyn_cast_or_null<IntegerAttr>();
|
|
|
|
if (!rhs)
|
|
|
|
return {};
|
|
|
|
|
|
|
|
// x % 1 = 0
|
|
|
|
if (rhs.getValue().isOneValue())
|
|
|
|
return IntegerAttr::get(rhs.getType(),
|
|
|
|
APInt(rhs.getValue().getBitWidth(), 0));
|
|
|
|
|
|
|
|
// Don't fold if it requires division by zero.
|
2019-05-16 12:51:45 -07:00
|
|
|
if (rhs.getValue().isNullValue())
|
2019-01-06 14:08:42 -08:00
|
|
|
return {};
|
|
|
|
|
|
|
|
auto lhs = operands.front().dyn_cast_or_null<IntegerAttr>();
|
|
|
|
if (!lhs)
|
|
|
|
return {};
|
|
|
|
|
|
|
|
return IntegerAttr::get(lhs.getType(), lhs.getValue().srem(rhs.getValue()));
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// RemIUOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2019-05-16 12:51:45 -07:00
|
|
|
OpFoldResult RemIUOp::fold(ArrayRef<Attribute> operands) {
|
2019-01-06 14:08:42 -08:00
|
|
|
assert(operands.size() == 2 && "remiu takes two operands");
|
|
|
|
|
|
|
|
auto rhs = operands.back().dyn_cast_or_null<IntegerAttr>();
|
|
|
|
if (!rhs)
|
|
|
|
return {};
|
|
|
|
|
|
|
|
// x % 1 = 0
|
|
|
|
if (rhs.getValue().isOneValue())
|
|
|
|
return IntegerAttr::get(rhs.getType(),
|
|
|
|
APInt(rhs.getValue().getBitWidth(), 0));
|
|
|
|
|
|
|
|
// Don't fold if it requires division by zero.
|
2019-05-16 12:51:45 -07:00
|
|
|
if (rhs.getValue().isNullValue())
|
2019-01-06 14:08:42 -08:00
|
|
|
return {};
|
|
|
|
|
|
|
|
auto lhs = operands.front().dyn_cast_or_null<IntegerAttr>();
|
|
|
|
if (!lhs)
|
|
|
|
return {};
|
|
|
|
|
|
|
|
return IntegerAttr::get(lhs.getType(), lhs.getValue().urem(rhs.getValue()));
|
|
|
|
}
|
|
|
|
|
2018-11-28 07:08:55 -08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2019-03-01 16:58:00 -08:00
|
|
|
// ReturnOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2019-05-13 11:53:14 -07:00
|
|
|
static ParseResult parseReturnOp(OpAsmParser *parser, OperationState *result) {
|
2019-03-01 16:58:00 -08:00
|
|
|
SmallVector<OpAsmParser::OperandType, 2> opInfo;
|
|
|
|
SmallVector<Type, 2> types;
|
|
|
|
llvm::SMLoc loc;
|
2019-05-06 22:01:31 -07:00
|
|
|
return failure(parser->getCurrentLocation(&loc) ||
|
|
|
|
parser->parseOperandList(opInfo) ||
|
|
|
|
(!opInfo.empty() && parser->parseColonTypeList(types)) ||
|
|
|
|
parser->resolveOperands(opInfo, types, loc, result->operands));
|
2019-03-01 16:58:00 -08:00
|
|
|
}
|
|
|
|
|
2019-05-14 08:27:32 -07:00
|
|
|
static void print(OpAsmPrinter *p, ReturnOp op) {
|
2019-03-01 16:58:00 -08:00
|
|
|
*p << "return";
|
2019-05-13 11:53:14 -07:00
|
|
|
if (op.getNumOperands() > 0) {
|
2019-03-01 16:58:00 -08:00
|
|
|
*p << ' ';
|
2019-05-13 11:53:14 -07:00
|
|
|
p->printOperands(op.operand_begin(), op.operand_end());
|
2019-03-01 16:58:00 -08:00
|
|
|
*p << " : ";
|
|
|
|
interleave(
|
2019-05-13 11:53:14 -07:00
|
|
|
op.operand_begin(), op.operand_end(),
|
2019-03-23 15:09:06 -07:00
|
|
|
[&](Value *e) { p->printType(e->getType()); }, [&]() { *p << ", "; });
|
2019-03-01 16:58:00 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-13 11:53:14 -07:00
|
|
|
static LogicalResult verify(ReturnOp op) {
|
|
|
|
auto *function = op.getOperation()->getFunction();
|
2019-03-01 16:58:00 -08:00
|
|
|
|
|
|
|
// The operand number and types must match the function signature.
|
|
|
|
const auto &results = function->getType().getResults();
|
2019-05-13 11:53:14 -07:00
|
|
|
if (op.getNumOperands() != results.size())
|
|
|
|
return op.emitOpError("has ")
|
|
|
|
<< op.getNumOperands()
|
|
|
|
<< " operands, but enclosing function returns " << results.size();
|
2019-03-01 16:58:00 -08:00
|
|
|
|
|
|
|
for (unsigned i = 0, e = results.size(); i != e; ++i)
|
2019-05-13 11:53:14 -07:00
|
|
|
if (op.getOperand(i)->getType() != results[i])
|
2019-05-16 14:12:18 -07:00
|
|
|
return op.emitError()
|
|
|
|
<< "type of return operand " << i << " ("
|
|
|
|
<< op.getOperand(i)->getType()
|
|
|
|
<< ") doesn't match function result type (" << results[i] << ")";
|
2019-03-01 16:58:00 -08:00
|
|
|
|
2019-04-02 13:09:34 -07:00
|
|
|
return success();
|
2019-03-01 16:58:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
2018-11-28 07:08:55 -08:00
|
|
|
// SelectOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
2018-11-28 15:09:39 -08:00
|
|
|
|
2019-05-24 18:01:20 -07:00
|
|
|
static ParseResult parseSelectOp(OpAsmParser *parser, OperationState *result) {
|
2018-11-28 07:08:55 -08:00
|
|
|
SmallVector<OpAsmParser::OperandType, 3> ops;
|
|
|
|
SmallVector<NamedAttribute, 4> attrs;
|
|
|
|
Type type;
|
|
|
|
|
|
|
|
if (parser->parseOperandList(ops, 3) ||
|
|
|
|
parser->parseOptionalAttributeDict(result->attributes) ||
|
|
|
|
parser->parseColonType(type))
|
2019-05-06 22:01:31 -07:00
|
|
|
return failure();
|
2018-11-28 07:08:55 -08:00
|
|
|
|
2019-01-15 09:30:39 -08:00
|
|
|
auto i1Type = getCheckedI1SameShape(&parser->getBuilder(), type);
|
|
|
|
if (!i1Type)
|
|
|
|
return parser->emitError(parser->getNameLoc(),
|
|
|
|
"expected type with valid i1 shape");
|
|
|
|
|
2018-11-28 07:08:55 -08:00
|
|
|
SmallVector<Type, 3> types = {i1Type, type, type};
|
2019-05-06 22:01:31 -07:00
|
|
|
return failure(parser->resolveOperands(ops, types, parser->getNameLoc(),
|
|
|
|
result->operands) ||
|
|
|
|
parser->addTypeToList(type, result->types));
|
2018-11-28 07:08:55 -08:00
|
|
|
}
|
|
|
|
|
2019-05-24 18:01:20 -07:00
|
|
|
static void print(OpAsmPrinter *p, SelectOp op) {
|
2019-03-02 18:03:03 -08:00
|
|
|
*p << "select ";
|
2019-05-24 18:01:20 -07:00
|
|
|
p->printOperands(op.getOperands());
|
|
|
|
*p << " : " << op.getTrueValue()->getType();
|
|
|
|
p->printOptionalAttrDict(op.getAttrs());
|
2018-11-28 07:08:55 -08:00
|
|
|
}
|
|
|
|
|
2019-05-24 18:01:20 -07:00
|
|
|
static LogicalResult verify(SelectOp op) {
|
|
|
|
auto trueType = op.getTrueValue()->getType();
|
|
|
|
auto falseType = op.getFalseValue()->getType();
|
2018-11-28 07:08:55 -08:00
|
|
|
|
|
|
|
if (trueType != falseType)
|
2019-05-24 18:01:20 -07:00
|
|
|
return op.emitOpError(
|
2018-11-28 07:08:55 -08:00
|
|
|
"requires 'true' and 'false' arguments to be of the same type");
|
|
|
|
|
2019-04-02 13:09:34 -07:00
|
|
|
return success();
|
2018-11-28 07:08:55 -08:00
|
|
|
}
|
|
|
|
|
2019-05-16 12:51:45 -07:00
|
|
|
OpFoldResult SelectOp::fold(ArrayRef<Attribute> operands) {
|
2019-02-07 08:26:31 -08:00
|
|
|
auto *condition = getCondition();
|
2018-11-28 07:08:55 -08:00
|
|
|
|
|
|
|
// select true, %0, %1 => %0
|
2019-02-07 08:26:31 -08:00
|
|
|
if (matchPattern(condition, m_One()))
|
|
|
|
return getTrueValue();
|
2018-11-28 07:08:55 -08:00
|
|
|
|
2019-02-07 08:26:31 -08:00
|
|
|
// select false, %0, %1 => %1
|
|
|
|
if (matchPattern(condition, m_Zero()))
|
|
|
|
return getFalseValue();
|
|
|
|
return nullptr;
|
2018-11-28 07:08:55 -08:00
|
|
|
}
|
|
|
|
|
2018-08-09 12:28:58 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// StoreOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2018-08-31 14:49:38 -07:00
|
|
|
void StoreOp::build(Builder *builder, OperationState *result,
|
2018-12-27 14:35:10 -08:00
|
|
|
Value *valueToStore, Value *memref,
|
|
|
|
ArrayRef<Value *> indices) {
|
2018-08-31 14:49:38 -07:00
|
|
|
result->addOperands(valueToStore);
|
|
|
|
result->addOperands(memref);
|
|
|
|
result->addOperands(indices);
|
|
|
|
}
|
|
|
|
|
2019-03-23 09:03:07 -07:00
|
|
|
void StoreOp::print(OpAsmPrinter *p) {
|
2018-07-31 14:11:38 -07:00
|
|
|
*p << "store " << *getValueToStore();
|
|
|
|
*p << ", " << *getMemRef() << '[';
|
|
|
|
p->printOperands(getIndices());
|
2018-08-02 16:54:36 -07:00
|
|
|
*p << ']';
|
|
|
|
p->printOptionalAttrDict(getAttrs());
|
2018-10-30 14:59:22 -07:00
|
|
|
*p << " : " << getMemRefType();
|
2018-07-31 14:11:38 -07:00
|
|
|
}
|
|
|
|
|
2019-05-06 22:01:31 -07:00
|
|
|
ParseResult StoreOp::parse(OpAsmParser *parser, OperationState *result) {
|
2018-07-31 14:11:38 -07:00
|
|
|
OpAsmParser::OperandType storeValueInfo;
|
|
|
|
OpAsmParser::OperandType memrefInfo;
|
|
|
|
SmallVector<OpAsmParser::OperandType, 4> indexInfo;
|
2018-10-30 14:59:22 -07:00
|
|
|
MemRefType memrefType;
|
2018-07-31 14:11:38 -07:00
|
|
|
|
2018-10-06 17:21:53 -07:00
|
|
|
auto affineIntTy = parser->getBuilder().getIndexType();
|
2019-05-06 22:01:31 -07:00
|
|
|
return failure(
|
|
|
|
parser->parseOperand(storeValueInfo) || parser->parseComma() ||
|
|
|
|
parser->parseOperand(memrefInfo) ||
|
|
|
|
parser->parseOperandList(indexInfo, -1, OpAsmParser::Delimiter::Square) ||
|
|
|
|
parser->parseOptionalAttributeDict(result->attributes) ||
|
|
|
|
parser->parseColonType(memrefType) ||
|
|
|
|
parser->resolveOperand(storeValueInfo, memrefType.getElementType(),
|
|
|
|
result->operands) ||
|
|
|
|
parser->resolveOperand(memrefInfo, memrefType, result->operands) ||
|
|
|
|
parser->resolveOperands(indexInfo, affineIntTy, result->operands));
|
2018-07-31 14:11:38 -07:00
|
|
|
}
|
|
|
|
|
2019-04-02 13:09:34 -07:00
|
|
|
LogicalResult StoreOp::verify() {
|
2018-07-31 14:11:38 -07:00
|
|
|
if (getNumOperands() < 2)
|
2018-09-09 20:40:23 -07:00
|
|
|
return emitOpError("expected a value to store and a memref");
|
2018-07-31 14:11:38 -07:00
|
|
|
|
|
|
|
// Second operand is a memref type.
|
2018-10-30 14:59:22 -07:00
|
|
|
auto memRefType = getMemRef()->getType().dyn_cast<MemRefType>();
|
2018-07-31 14:11:38 -07:00
|
|
|
if (!memRefType)
|
2018-09-09 20:40:23 -07:00
|
|
|
return emitOpError("second operand must be a memref");
|
2018-07-31 14:11:38 -07:00
|
|
|
|
|
|
|
// First operand must have same type as memref element type.
|
2018-10-30 14:59:22 -07:00
|
|
|
if (getValueToStore()->getType() != memRefType.getElementType())
|
2018-09-09 20:40:23 -07:00
|
|
|
return emitOpError("first operand must have same type memref element type");
|
2018-07-31 14:11:38 -07:00
|
|
|
|
2018-10-30 14:59:22 -07:00
|
|
|
if (getNumOperands() != 2 + memRefType.getRank())
|
2018-09-09 20:40:23 -07:00
|
|
|
return emitOpError("store index operand count not equal to memref rank");
|
2018-07-31 14:11:38 -07:00
|
|
|
|
|
|
|
for (auto *idx : getIndices())
|
2018-10-30 14:59:22 -07:00
|
|
|
if (!idx->getType().isIndex())
|
2018-10-06 17:21:53 -07:00
|
|
|
return emitOpError("index to load must have 'index' type");
|
2018-07-31 14:11:38 -07:00
|
|
|
|
|
|
|
// TODO: Verify we have the right number of indices.
|
|
|
|
|
2018-12-28 08:48:09 -08:00
|
|
|
// TODO: in Function verify that the indices are parameters, IV's, or the
|
2019-02-06 11:08:18 -08:00
|
|
|
// result of an affine.apply.
|
2019-04-02 13:09:34 -07:00
|
|
|
return success();
|
2018-07-31 14:11:38 -07:00
|
|
|
}
|
|
|
|
|
2018-11-28 15:09:39 -08:00
|
|
|
void StoreOp::getCanonicalizationPatterns(OwningRewritePatternList &results,
|
2018-10-25 16:44:04 -07:00
|
|
|
MLIRContext *context) {
|
|
|
|
/// store(memrefcast) -> store
|
|
|
|
results.push_back(
|
2019-03-19 08:45:06 -07:00
|
|
|
llvm::make_unique<MemRefCastFolder>(getOperationName(), context));
|
2018-10-25 16:44:04 -07:00
|
|
|
}
|
|
|
|
|
2018-10-03 09:43:13 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// SubFOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2019-05-16 12:51:45 -07:00
|
|
|
OpFoldResult SubFOp::fold(ArrayRef<Attribute> operands) {
|
2019-01-11 09:12:11 -08:00
|
|
|
return constFoldBinaryOp<FloatAttr>(
|
|
|
|
operands, [](APFloat a, APFloat b) { return a - b; });
|
2018-10-03 09:43:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// SubIOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2019-05-16 12:51:45 -07:00
|
|
|
OpFoldResult SubIOp::fold(ArrayRef<Attribute> operands) {
|
|
|
|
// subi(x,x) -> 0
|
|
|
|
if (getOperand(0) == getOperand(1))
|
|
|
|
return Builder(getContext()).getZeroAttr(getType());
|
|
|
|
|
2019-01-11 09:12:11 -08:00
|
|
|
return constFoldBinaryOp<IntegerAttr>(operands,
|
|
|
|
[](APInt a, APInt b) { return a - b; });
|
2018-10-03 09:43:13 -07:00
|
|
|
}
|
2018-10-25 16:44:04 -07:00
|
|
|
|
2019-04-08 00:00:46 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// AndOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2019-05-16 12:51:45 -07:00
|
|
|
OpFoldResult AndOp::fold(ArrayRef<Attribute> operands) {
|
2019-04-08 00:00:46 -07:00
|
|
|
/// and(x, 0) -> 0
|
|
|
|
if (matchPattern(rhs(), m_Zero()))
|
|
|
|
return rhs();
|
|
|
|
/// and(x,x) -> x
|
|
|
|
if (lhs() == rhs())
|
|
|
|
return rhs();
|
|
|
|
|
2019-05-16 12:51:45 -07:00
|
|
|
return constFoldBinaryOp<IntegerAttr>(operands,
|
|
|
|
[](APInt a, APInt b) { return a & b; });
|
2019-04-08 00:00:46 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// OrOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2019-05-16 12:51:45 -07:00
|
|
|
OpFoldResult OrOp::fold(ArrayRef<Attribute> operands) {
|
2019-04-08 00:00:46 -07:00
|
|
|
/// or(x, 0) -> x
|
|
|
|
if (matchPattern(rhs(), m_Zero()))
|
|
|
|
return lhs();
|
|
|
|
/// or(x,x) -> x
|
|
|
|
if (lhs() == rhs())
|
|
|
|
return rhs();
|
|
|
|
|
2019-05-16 12:51:45 -07:00
|
|
|
return constFoldBinaryOp<IntegerAttr>(operands,
|
|
|
|
[](APInt a, APInt b) { return a | b; });
|
2019-04-08 00:00:46 -07:00
|
|
|
}
|
|
|
|
|
2019-04-08 05:53:59 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// XOrOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2019-05-16 12:51:45 -07:00
|
|
|
OpFoldResult XOrOp::fold(ArrayRef<Attribute> operands) {
|
2019-04-08 05:53:59 -07:00
|
|
|
/// xor(x, 0) -> x
|
|
|
|
if (matchPattern(rhs(), m_Zero()))
|
|
|
|
return lhs();
|
2019-05-16 12:51:45 -07:00
|
|
|
/// xor(x,x) -> 0
|
|
|
|
if (lhs() == rhs())
|
|
|
|
return Builder(getContext()).getZeroAttr(getType());
|
2019-04-08 05:53:59 -07:00
|
|
|
|
2019-05-16 12:51:45 -07:00
|
|
|
return constFoldBinaryOp<IntegerAttr>(operands,
|
|
|
|
[](APInt a, APInt b) { return a ^ b; });
|
2019-04-08 05:53:59 -07:00
|
|
|
}
|
|
|
|
|
2018-10-25 16:44:04 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// TensorCastOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2019-04-27 20:55:38 -07:00
|
|
|
bool TensorCastOp::areCastCompatible(Type a, Type b) {
|
|
|
|
auto aT = a.dyn_cast<TensorType>();
|
|
|
|
auto bT = b.dyn_cast<TensorType>();
|
|
|
|
if (!aT || !bT)
|
|
|
|
return false;
|
2018-10-25 16:44:04 -07:00
|
|
|
|
2019-04-27 20:55:38 -07:00
|
|
|
if (aT.getElementType() != bT.getElementType())
|
|
|
|
return false;
|
2018-10-25 16:44:04 -07:00
|
|
|
|
2019-04-27 20:55:38 -07:00
|
|
|
// If the either are unranked, then the cast is valid.
|
|
|
|
auto aRType = aT.dyn_cast<RankedTensorType>();
|
|
|
|
auto bRType = bT.dyn_cast<RankedTensorType>();
|
|
|
|
if (!aRType || !bRType)
|
|
|
|
return true;
|
2018-10-25 16:44:04 -07:00
|
|
|
|
|
|
|
// If they are both ranked, they have to have the same rank, and any specified
|
|
|
|
// dimensions must match.
|
2019-04-27 20:55:38 -07:00
|
|
|
if (aRType.getRank() != bRType.getRank())
|
|
|
|
return false;
|
2018-10-25 16:44:04 -07:00
|
|
|
|
2019-04-27 20:55:38 -07:00
|
|
|
for (unsigned i = 0, e = aRType.getRank(); i != e; ++i) {
|
|
|
|
int64_t aDim = aRType.getDimSize(i), bDim = bRType.getDimSize(i);
|
|
|
|
if (aDim != -1 && bDim != -1 && aDim != bDim)
|
|
|
|
return false;
|
2018-10-25 16:44:04 -07:00
|
|
|
}
|
|
|
|
|
2019-04-27 20:55:38 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-05-16 12:51:45 -07:00
|
|
|
OpFoldResult TensorCastOp::fold(ArrayRef<Attribute> operands) {
|
|
|
|
return impl::foldCastOp(*this);
|
|
|
|
}
|
2019-03-20 17:25:34 -07:00
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// TableGen'd op method definitions
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#define GET_OP_CLASSES
|
|
|
|
#include "mlir/StandardOps/Ops.cpp.inc"
|