2018-07-05 11:45:39 +08:00
|
|
|
//===- Operation.cpp - MLIR Operation Class -------------------------------===//
|
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
// =============================================================================
|
|
|
|
|
|
|
|
#include "mlir/IR/Operation.h"
|
2018-07-06 12:20:59 +08:00
|
|
|
#include "AttributeListStorage.h"
|
2018-08-21 23:42:19 +08:00
|
|
|
#include "mlir/IR/CFGFunction.h"
|
2018-07-23 12:02:26 +08:00
|
|
|
#include "mlir/IR/Instructions.h"
|
2018-08-21 23:42:19 +08:00
|
|
|
#include "mlir/IR/MLFunction.h"
|
2018-08-02 01:18:59 +08:00
|
|
|
#include "mlir/IR/MLIRContext.h"
|
2018-09-10 11:40:23 +08:00
|
|
|
#include "mlir/IR/OpDefinition.h"
|
2018-09-27 01:07:16 +08:00
|
|
|
#include "mlir/IR/OpImplementation.h"
|
2018-07-23 12:02:26 +08:00
|
|
|
#include "mlir/IR/Statements.h"
|
2018-07-05 11:45:39 +08:00
|
|
|
using namespace mlir;
|
|
|
|
|
2018-10-10 13:08:52 +08:00
|
|
|
/// Form the OperationName for an op with the specified string. This either is
|
|
|
|
/// a reference to an AbstractOperation if one is known, or a uniqued Identifier
|
|
|
|
/// if not.
|
|
|
|
OperationName::OperationName(StringRef name, MLIRContext *context) {
|
2018-10-22 10:49:31 +08:00
|
|
|
if (auto *op = AbstractOperation::lookup(name, context))
|
2018-10-10 13:08:52 +08:00
|
|
|
representation = op;
|
|
|
|
else
|
|
|
|
representation = Identifier::get(name, context);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the name of this operation. This always succeeds.
|
|
|
|
StringRef OperationName::getStringRef() const {
|
|
|
|
if (auto *op = representation.dyn_cast<const AbstractOperation *>())
|
|
|
|
return op->name;
|
|
|
|
return representation.get<Identifier>().strref();
|
|
|
|
}
|
|
|
|
|
|
|
|
const AbstractOperation *OperationName::getAbstractOperation() const {
|
|
|
|
return representation.dyn_cast<const AbstractOperation *>();
|
|
|
|
}
|
|
|
|
|
|
|
|
OperationName OperationName::getFromOpaquePointer(void *pointer) {
|
|
|
|
return OperationName(RepresentationUnion::getFromOpaqueValue(pointer));
|
|
|
|
}
|
|
|
|
|
2018-10-22 10:49:31 +08:00
|
|
|
OpAsmParser::~OpAsmParser() {}
|
|
|
|
|
2018-10-10 13:08:52 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Operation class
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
Operation::Operation(bool isInstruction, OperationName name,
|
2018-07-19 10:06:45 +08:00
|
|
|
ArrayRef<NamedAttribute> attrs, MLIRContext *context)
|
|
|
|
: nameAndIsInstruction(name, isInstruction) {
|
2018-07-06 12:20:59 +08:00
|
|
|
this->attrs = AttributeListStorage::get(attrs, context);
|
|
|
|
|
2018-07-05 11:45:39 +08:00
|
|
|
#ifndef NDEBUG
|
|
|
|
for (auto elt : attrs)
|
|
|
|
assert(elt.second != nullptr && "Attributes cannot have null entries");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2018-07-23 12:02:26 +08:00
|
|
|
Operation::~Operation() {}
|
|
|
|
|
2018-08-02 01:18:59 +08:00
|
|
|
/// Return the context this operation is associated with.
|
|
|
|
MLIRContext *Operation::getContext() const {
|
2018-10-20 00:07:58 +08:00
|
|
|
if (auto *inst = llvm::dyn_cast<OperationInst>(this))
|
2018-08-02 01:18:59 +08:00
|
|
|
return inst->getContext();
|
2018-10-20 00:07:58 +08:00
|
|
|
return llvm::cast<OperationStmt>(this)->getContext();
|
2018-08-02 01:18:59 +08:00
|
|
|
}
|
|
|
|
|
2018-08-24 05:32:25 +08:00
|
|
|
/// The source location the operation was defined or derived from. Note that
|
|
|
|
/// it is possible for this pointer to be null.
|
2018-08-28 12:05:16 +08:00
|
|
|
Location *Operation::getLoc() const {
|
2018-10-20 00:07:58 +08:00
|
|
|
if (auto *inst = llvm::dyn_cast<OperationInst>(this))
|
2018-08-24 05:32:25 +08:00
|
|
|
return inst->getLoc();
|
2018-10-20 00:07:58 +08:00
|
|
|
return llvm::cast<OperationStmt>(this)->getLoc();
|
2018-08-24 05:32:25 +08:00
|
|
|
}
|
|
|
|
|
2018-08-21 23:42:19 +08:00
|
|
|
/// Return the function this operation is defined in.
|
|
|
|
Function *Operation::getOperationFunction() {
|
2018-10-20 00:07:58 +08:00
|
|
|
if (auto *inst = llvm::dyn_cast<OperationInst>(this))
|
2018-08-21 23:42:19 +08:00
|
|
|
return inst->getFunction();
|
2018-10-20 00:07:58 +08:00
|
|
|
return llvm::cast<OperationStmt>(this)->findFunction();
|
2018-08-21 23:42:19 +08:00
|
|
|
}
|
|
|
|
|
2018-07-23 12:02:26 +08:00
|
|
|
/// Return the number of operands this operation has.
|
|
|
|
unsigned Operation::getNumOperands() const {
|
2018-10-20 00:07:58 +08:00
|
|
|
if (auto *inst = llvm::dyn_cast<OperationInst>(this))
|
2018-07-23 12:02:26 +08:00
|
|
|
return inst->getNumOperands();
|
2018-08-21 23:42:19 +08:00
|
|
|
|
2018-10-20 00:07:58 +08:00
|
|
|
return llvm::cast<OperationStmt>(this)->getNumOperands();
|
2018-07-23 12:02:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
SSAValue *Operation::getOperand(unsigned idx) {
|
2018-10-20 00:07:58 +08:00
|
|
|
if (auto *inst = llvm::dyn_cast<OperationInst>(this))
|
2018-07-23 12:02:26 +08:00
|
|
|
return inst->getOperand(idx);
|
2018-08-21 23:42:19 +08:00
|
|
|
|
2018-10-20 00:07:58 +08:00
|
|
|
return llvm::cast<OperationStmt>(this)->getOperand(idx);
|
2018-07-23 12:02:26 +08:00
|
|
|
}
|
|
|
|
|
2018-07-24 01:08:00 +08:00
|
|
|
void Operation::setOperand(unsigned idx, SSAValue *value) {
|
2018-10-20 00:07:58 +08:00
|
|
|
if (auto *inst = llvm::dyn_cast<OperationInst>(this)) {
|
|
|
|
inst->setOperand(idx, llvm::cast<CFGValue>(value));
|
2018-07-24 01:08:00 +08:00
|
|
|
} else {
|
2018-10-20 00:07:58 +08:00
|
|
|
auto *stmt = llvm::cast<OperationStmt>(this);
|
|
|
|
stmt->setOperand(idx, llvm::cast<MLValue>(value));
|
2018-07-24 01:08:00 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-23 12:02:26 +08:00
|
|
|
/// Return the number of results this operation has.
|
|
|
|
unsigned Operation::getNumResults() const {
|
2018-10-20 00:07:58 +08:00
|
|
|
if (auto *inst = llvm::dyn_cast<OperationInst>(this))
|
2018-07-23 12:02:26 +08:00
|
|
|
return inst->getNumResults();
|
2018-08-21 23:42:19 +08:00
|
|
|
|
2018-10-20 00:07:58 +08:00
|
|
|
return llvm::cast<OperationStmt>(this)->getNumResults();
|
2018-07-23 12:02:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the indicated result.
|
|
|
|
SSAValue *Operation::getResult(unsigned idx) {
|
2018-10-20 00:07:58 +08:00
|
|
|
if (auto *inst = llvm::dyn_cast<OperationInst>(this))
|
2018-07-23 12:02:26 +08:00
|
|
|
return inst->getResult(idx);
|
2018-08-21 23:42:19 +08:00
|
|
|
|
2018-10-20 00:07:58 +08:00
|
|
|
return llvm::cast<OperationStmt>(this)->getResult(idx);
|
2018-07-05 11:45:39 +08:00
|
|
|
}
|
|
|
|
|
2018-10-17 00:31:45 +08:00
|
|
|
/// Return true if there are no users of any results of this operation.
|
|
|
|
bool Operation::use_empty() const {
|
|
|
|
for (auto *result : getResults())
|
|
|
|
if (!result->use_empty())
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-07-06 12:20:59 +08:00
|
|
|
ArrayRef<NamedAttribute> Operation::getAttrs() const {
|
|
|
|
if (!attrs)
|
|
|
|
return {};
|
|
|
|
return attrs->getElements();
|
|
|
|
}
|
|
|
|
|
2018-07-06 00:12:11 +08:00
|
|
|
/// If an attribute exists with the specified name, change it to the new
|
|
|
|
/// value. Otherwise, add a new attribute with the specified name/value.
|
2018-08-06 12:12:29 +08:00
|
|
|
void Operation::setAttr(Identifier name, Attribute *value) {
|
2018-07-06 00:12:11 +08:00
|
|
|
assert(value && "attributes may never be null");
|
2018-07-06 12:20:59 +08:00
|
|
|
auto origAttrs = getAttrs();
|
|
|
|
|
|
|
|
SmallVector<NamedAttribute, 8> newAttrs(origAttrs.begin(), origAttrs.end());
|
2018-08-06 12:12:29 +08:00
|
|
|
auto *context = getContext();
|
2018-07-06 12:20:59 +08:00
|
|
|
|
2018-07-06 00:12:11 +08:00
|
|
|
// If we already have this attribute, replace it.
|
2018-07-06 12:20:59 +08:00
|
|
|
for (auto &elt : newAttrs)
|
2018-07-06 00:12:11 +08:00
|
|
|
if (elt.first == name) {
|
|
|
|
elt.second = value;
|
2018-07-06 12:20:59 +08:00
|
|
|
attrs = AttributeListStorage::get(newAttrs, context);
|
2018-07-06 00:12:11 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, add it.
|
2018-07-06 12:20:59 +08:00
|
|
|
newAttrs.push_back({name, value});
|
|
|
|
attrs = AttributeListStorage::get(newAttrs, context);
|
2018-07-06 00:12:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Remove the attribute with the specified name if it exists. The return
|
|
|
|
/// value indicates whether the attribute was present or not.
|
2018-08-06 12:12:29 +08:00
|
|
|
auto Operation::removeAttr(Identifier name) -> RemoveResult {
|
2018-07-06 12:20:59 +08:00
|
|
|
auto origAttrs = getAttrs();
|
|
|
|
for (unsigned i = 0, e = origAttrs.size(); i != e; ++i) {
|
|
|
|
if (origAttrs[i].first == name) {
|
|
|
|
SmallVector<NamedAttribute, 8> newAttrs;
|
|
|
|
newAttrs.reserve(origAttrs.size() - 1);
|
|
|
|
newAttrs.append(origAttrs.begin(), origAttrs.begin() + i);
|
|
|
|
newAttrs.append(origAttrs.begin() + i + 1, origAttrs.end());
|
2018-08-06 12:12:29 +08:00
|
|
|
attrs = AttributeListStorage::get(newAttrs, getContext());
|
2018-07-05 11:45:39 +08:00
|
|
|
return RemoveResult::Removed;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return RemoveResult::NotFound;
|
|
|
|
}
|
2018-08-02 01:18:59 +08:00
|
|
|
|
2018-08-06 12:12:29 +08:00
|
|
|
/// Emit a note about this operation, reporting up to any diagnostic
|
|
|
|
/// handlers that may be listening.
|
|
|
|
void Operation::emitNote(const Twine &message) const {
|
2018-08-24 05:32:25 +08:00
|
|
|
getContext()->emitDiagnostic(getLoc(), message,
|
2018-08-06 12:12:29 +08:00
|
|
|
MLIRContext::DiagnosticKind::Note);
|
|
|
|
}
|
|
|
|
|
2018-08-02 01:18:59 +08:00
|
|
|
/// Emit a warning about this operation, reporting up to any diagnostic
|
|
|
|
/// handlers that may be listening.
|
|
|
|
void Operation::emitWarning(const Twine &message) const {
|
2018-08-24 05:32:25 +08:00
|
|
|
getContext()->emitDiagnostic(getLoc(), message,
|
2018-08-06 12:12:29 +08:00
|
|
|
MLIRContext::DiagnosticKind::Warning);
|
2018-08-02 01:18:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Emit an error about fatal conditions with this operation, reporting up to
|
|
|
|
/// any diagnostic handlers that may be listening. NOTE: This may terminate
|
|
|
|
/// the containing application, only use when the IR is in an inconsistent
|
|
|
|
/// state.
|
|
|
|
void Operation::emitError(const Twine &message) const {
|
2018-08-24 05:32:25 +08:00
|
|
|
getContext()->emitDiagnostic(getLoc(), message,
|
2018-08-06 12:12:29 +08:00
|
|
|
MLIRContext::DiagnosticKind::Error);
|
2018-08-02 01:18:59 +08:00
|
|
|
}
|
2018-09-10 11:40:23 +08:00
|
|
|
|
|
|
|
/// Emit an error with the op name prefixed, like "'dim' op " which is
|
|
|
|
/// convenient for verifiers.
|
|
|
|
bool Operation::emitOpError(const Twine &message) const {
|
2018-10-10 13:08:52 +08:00
|
|
|
emitError(Twine('\'') + getName().getStringRef() + "' op " + message);
|
2018-09-10 11:40:23 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-10-22 10:53:10 +08:00
|
|
|
/// Remove this operation from its parent block and delete it.
|
|
|
|
void Operation::erase() {
|
|
|
|
if (auto *inst = llvm::dyn_cast<OperationInst>(this))
|
|
|
|
return inst->erase();
|
|
|
|
return llvm::cast<OperationStmt>(this)->erase();
|
|
|
|
}
|
|
|
|
|
2018-09-20 12:35:11 +08:00
|
|
|
/// Attempt to constant fold this operation with the specified constant
|
|
|
|
/// operand values. If successful, this returns false and fills in the
|
|
|
|
/// results vector. If not, this returns true and results is unspecified.
|
|
|
|
bool Operation::constantFold(ArrayRef<Attribute *> operands,
|
|
|
|
SmallVectorImpl<Attribute *> &results) const {
|
|
|
|
// If we have a registered operation definition matching this one, use it to
|
|
|
|
// try to constant fold the operation.
|
|
|
|
if (auto *abstractOp = getAbstractOperation())
|
|
|
|
if (!abstractOp->constantFoldHook(this, operands, results))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// TODO: Otherwise, fall back on the dialect hook to handle it.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-10-23 04:08:27 +08:00
|
|
|
/// Methods for support type inquiry through isa, cast, and dyn_cast.
|
|
|
|
bool Operation::classof(const Instruction *inst) {
|
|
|
|
return inst->getKind() == Instruction::Kind::Operation;
|
|
|
|
}
|
|
|
|
bool Operation::classof(const Statement *stmt) {
|
|
|
|
return stmt->getKind() == Statement::Kind::Operation;
|
|
|
|
}
|
|
|
|
|
2018-09-10 11:40:23 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2018-09-27 06:06:38 +08:00
|
|
|
// OpState trait class.
|
2018-09-10 11:40:23 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2018-10-22 10:49:31 +08:00
|
|
|
// The fallback for the parser is to reject the short form.
|
|
|
|
bool OpState::parse(OpAsmParser *parser, OperationState *result) {
|
|
|
|
return parser->emitError(parser->getNameLoc(), "has no concise form");
|
|
|
|
}
|
|
|
|
|
|
|
|
// The fallback for the printer is to print it the longhand form.
|
|
|
|
void OpState::print(OpAsmPrinter *p) const {
|
|
|
|
p->printDefaultOp(getOperation());
|
|
|
|
}
|
|
|
|
|
2018-09-10 11:40:23 +08:00
|
|
|
/// Emit an error about fatal conditions with this operation, reporting up to
|
|
|
|
/// any diagnostic handlers that may be listening. NOTE: This may terminate
|
|
|
|
/// the containing application, only use when the IR is in an inconsistent
|
|
|
|
/// state.
|
2018-09-27 06:06:38 +08:00
|
|
|
void OpState::emitError(const Twine &message) const {
|
2018-09-10 11:40:23 +08:00
|
|
|
getOperation()->emitError(message);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Emit an error with the op name prefixed, like "'dim' op " which is
|
|
|
|
/// convenient for verifiers.
|
2018-09-27 06:06:38 +08:00
|
|
|
bool OpState::emitOpError(const Twine &message) const {
|
2018-09-10 11:40:23 +08:00
|
|
|
return getOperation()->emitOpError(message);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Emit a warning about this operation, reporting up to any diagnostic
|
|
|
|
/// handlers that may be listening.
|
2018-09-27 06:06:38 +08:00
|
|
|
void OpState::emitWarning(const Twine &message) const {
|
2018-09-10 11:40:23 +08:00
|
|
|
getOperation()->emitWarning(message);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Emit a note about this operation, reporting up to any diagnostic
|
|
|
|
/// handlers that may be listening.
|
2018-09-27 06:06:38 +08:00
|
|
|
void OpState::emitNote(const Twine &message) const {
|
2018-09-10 11:40:23 +08:00
|
|
|
getOperation()->emitNote(message);
|
|
|
|
}
|
2018-09-27 01:07:16 +08:00
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Op Trait implementations
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2018-09-27 12:18:42 +08:00
|
|
|
bool OpTrait::impl::verifyZeroOperands(const Operation *op) {
|
|
|
|
if (op->getNumOperands() != 0)
|
|
|
|
return op->emitOpError("requires zero operands");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool OpTrait::impl::verifyOneOperand(const Operation *op) {
|
|
|
|
if (op->getNumOperands() != 1)
|
|
|
|
return op->emitOpError("requires a single operand");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool OpTrait::impl::verifyNOperands(const Operation *op, unsigned numOperands) {
|
2018-10-10 06:04:27 +08:00
|
|
|
if (op->getNumOperands() != numOperands) {
|
|
|
|
return op->emitOpError("expected " + Twine(numOperands) +
|
|
|
|
" operands, but found " +
|
|
|
|
Twine(op->getNumOperands()));
|
|
|
|
}
|
2018-09-27 12:18:42 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool OpTrait::impl::verifyAtLeastNOperands(const Operation *op,
|
|
|
|
unsigned numOperands) {
|
|
|
|
if (op->getNumOperands() < numOperands)
|
|
|
|
return op->emitOpError("expected " + Twine(numOperands) +
|
|
|
|
" or more operands");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool OpTrait::impl::verifyZeroResult(const Operation *op) {
|
|
|
|
if (op->getNumResults() != 0)
|
|
|
|
return op->emitOpError("requires zero results");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool OpTrait::impl::verifyOneResult(const Operation *op) {
|
|
|
|
if (op->getNumResults() != 1)
|
|
|
|
return op->emitOpError("requires one result");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool OpTrait::impl::verifyNResults(const Operation *op, unsigned numOperands) {
|
|
|
|
if (op->getNumResults() != numOperands)
|
|
|
|
return op->emitOpError("expected " + Twine(numOperands) + " results");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool OpTrait::impl::verifyAtLeastNResults(const Operation *op,
|
|
|
|
unsigned numOperands) {
|
|
|
|
if (op->getNumResults() < numOperands)
|
|
|
|
return op->emitOpError("expected " + Twine(numOperands) +
|
|
|
|
" or more results");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-09-27 01:07:16 +08:00
|
|
|
bool OpTrait::impl::verifySameOperandsAndResult(const Operation *op) {
|
|
|
|
auto *type = op->getResult(0)->getType();
|
|
|
|
for (unsigned i = 1, e = op->getNumResults(); i < e; ++i) {
|
|
|
|
if (op->getResult(i)->getType() != type)
|
|
|
|
return op->emitOpError(
|
|
|
|
"requires the same type for all operands and results");
|
|
|
|
}
|
|
|
|
for (unsigned i = 0, e = op->getNumOperands(); i < e; ++i) {
|
|
|
|
if (op->getOperand(i)->getType() != type)
|
|
|
|
return op->emitOpError(
|
|
|
|
"requires the same type for all operands and results");
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// If this is a vector type, or a tensor type, return the scalar element type
|
|
|
|
/// that it is built around, otherwise return the type unmodified.
|
|
|
|
static Type *getTensorOrVectorElementType(Type *type) {
|
|
|
|
if (auto *vec = dyn_cast<VectorType>(type))
|
|
|
|
return vec->getElementType();
|
|
|
|
|
|
|
|
// Look through tensor<vector<...>> to find the underlying element type.
|
|
|
|
if (auto *tensor = dyn_cast<TensorType>(type))
|
|
|
|
return getTensorOrVectorElementType(tensor->getElementType());
|
|
|
|
return type;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool OpTrait::impl::verifyResultsAreFloatLike(const Operation *op) {
|
|
|
|
for (auto *result : op->getResults()) {
|
|
|
|
if (!isa<FloatType>(getTensorOrVectorElementType(result->getType())))
|
|
|
|
return op->emitOpError("requires a floating point type");
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool OpTrait::impl::verifyResultsAreIntegerLike(const Operation *op) {
|
|
|
|
for (auto *result : op->getResults()) {
|
|
|
|
if (!isa<IntegerType>(getTensorOrVectorElementType(result->getType())))
|
|
|
|
return op->emitOpError("requires an integer type");
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// BinaryOp implementation
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
// These functions are out-of-line implementations of the methods in BinaryOp,
|
|
|
|
// which avoids them being template instantiated/duplicated.
|
|
|
|
|
|
|
|
void impl::buildBinaryOp(Builder *builder, OperationState *result,
|
|
|
|
SSAValue *lhs, SSAValue *rhs) {
|
|
|
|
assert(lhs->getType() == rhs->getType());
|
|
|
|
result->addOperands({lhs, rhs});
|
|
|
|
result->types.push_back(lhs->getType());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool impl::parseBinaryOp(OpAsmParser *parser, OperationState *result) {
|
|
|
|
SmallVector<OpAsmParser::OperandType, 2> ops;
|
|
|
|
Type *type;
|
|
|
|
return parser->parseOperandList(ops, 2) ||
|
|
|
|
parser->parseOptionalAttributeDict(result->attributes) ||
|
|
|
|
parser->parseColonType(type) ||
|
|
|
|
parser->resolveOperands(ops, type, result->operands) ||
|
|
|
|
parser->addTypeToList(type, result->types);
|
|
|
|
}
|
|
|
|
|
|
|
|
void impl::printBinaryOp(const Operation *op, OpAsmPrinter *p) {
|
2018-10-23 00:00:03 +08:00
|
|
|
*p << op->getName() << ' ' << *op->getOperand(0) << ", "
|
2018-09-27 01:07:16 +08:00
|
|
|
<< *op->getOperand(1);
|
|
|
|
p->printOptionalAttrDict(op->getAttrs());
|
|
|
|
*p << " : " << *op->getResult(0)->getType();
|
|
|
|
}
|
2018-10-23 00:00:03 +08:00
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// CastOp implementation
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
void impl::buildCastOp(Builder *builder, OperationState *result,
|
|
|
|
SSAValue *source, Type *destType) {
|
|
|
|
result->addOperands(source);
|
|
|
|
result->addTypes(destType);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool impl::parseCastOp(OpAsmParser *parser, OperationState *result) {
|
|
|
|
OpAsmParser::OperandType srcInfo;
|
|
|
|
Type *srcType, *dstType;
|
|
|
|
return parser->parseOperand(srcInfo) || parser->parseColonType(srcType) ||
|
|
|
|
parser->resolveOperand(srcInfo, srcType, result->operands) ||
|
|
|
|
parser->parseKeywordType("to", dstType) ||
|
|
|
|
parser->addTypeToList(dstType, result->types);
|
|
|
|
}
|
|
|
|
|
|
|
|
void impl::printCastOp(const Operation *op, OpAsmPrinter *p) {
|
|
|
|
*p << op->getName() << ' ' << *op->getOperand(0) << " : "
|
|
|
|
<< *op->getOperand(0)->getType() << " to " << *op->getResult(0)->getType();
|
|
|
|
}
|