TableGen standard arithmetic ops

Use tablegen to generate definitions of the standard binary arithmetic
operations.  These operations share a lot of boilerplate that is better off
generated by a tool.

Using tablegen for standard binary arithmetic operations requires the following
modifications.
1. Add a bit field `hasConstantFolder` to the base Op tablegen class; generate
the `constantFold` method signature if the bit is set.  Differentiate between
single-result and zero/multi-result functions that use different signatures.
The implementation of the method remains in C++, similarly to canonicalization
patterns, since it may be large and non-trivial.
2. Define the `AnyType` record of class `Type` since `BinaryOp` currently
provided in op_base.td is supposed to operate on tensors and other tablegen
users may rely on this behavior.

Note that this drops the inline documentation on the operation classes that was
copy-pasted around anyway.  Since we don't generate g3doc from tablegen yet,
keep LangRef.md as it is.  Eventually, the user documentation can move to the
tablegen definition file as well.

PiperOrigin-RevId: 227820815
This commit is contained in:
Alex Zinenko 2019-01-04 01:34:16 -08:00 committed by jpienaar
parent dde5bf234d
commit 8281151c2a
6 changed files with 147 additions and 174 deletions

View File

@ -898,34 +898,6 @@ bool parseBinaryOp(OpAsmParser *parser, OperationState *result);
void printBinaryOp(const OperationInst *op, OpAsmPrinter *p);
} // namespace impl
/// This template is used for operations that are simple binary ops that have
/// two input operands, one result, and whose operands and results all have
/// the same type.
///
/// From this structure, subclasses get a standard builder, parser and printer.
///
template <typename ConcreteType, template <typename T> class... Traits>
class BinaryOp
: public Op<ConcreteType, OpTrait::NOperands<2>::Impl, OpTrait::OneResult,
OpTrait::SameOperandsAndResultType, Traits...> {
public:
static void build(Builder *builder, OperationState *result, Value *lhs,
Value *rhs) {
impl::buildBinaryOp(builder, result, lhs, rhs);
}
static bool parse(OpAsmParser *parser, OperationState *result) {
return impl::parseBinaryOp(parser, result);
}
void print(OpAsmPrinter *p) const {
return impl::printBinaryOp(this->getInstruction(), p);
}
protected:
explicit BinaryOp(const OperationInst *state)
: Op<ConcreteType, OpTrait::NOperands<2>::Impl, OpTrait::OneResult,
OpTrait::SameOperandsAndResultType, Traits...>(state) {}
};
// These functions are out-of-line implementations of the methods in CastOp,
// which avoids them being template instantiated/duplicated.
namespace impl {

View File

@ -208,6 +208,9 @@ class Op<string mnemonic, list<OpProperty> props = []> {
// and C++ implementations.
bit hasCanonicalizationPatterns = 0b0;
// Whether this op has a constant folder.
bit hasConstantFolder = 0b0;
// Op properties.
list<OpProperty> properties = props;
}

View File

@ -37,53 +37,8 @@ public:
StandardOpsDialect(MLIRContext *context);
};
/// The "addf" operation takes two operands and returns one result, each of
/// these is required to be of the same type. This type may be a floating point
/// scalar type, a vector whose element type is a floating point type, or a
/// floating point tensor. For example:
///
/// %2 = addf %0, %1 : f32
///
class AddFOp
: public BinaryOp<AddFOp, OpTrait::ResultsAreFloatLike,
OpTrait::IsCommutative, OpTrait::HasNoSideEffect> {
public:
static void build(Builder *builder, OperationState *result, Value *lhs,
Value *rhs);
static StringRef getOperationName() { return "addf"; }
Attribute constantFold(ArrayRef<Attribute> operands,
MLIRContext *context) const;
private:
friend class OperationInst;
explicit AddFOp(const OperationInst *state) : BinaryOp(state) {}
};
/// The "addi" operation takes two operands and returns one result, each of
/// these is required to be of the same type. This type may be an integer
/// scalar type, a vector whose element type is an integer type, or a
/// integer tensor. For example:
///
/// %2 = addi %0, %1 : i32
///
class AddIOp
: public BinaryOp<AddIOp, OpTrait::ResultsAreIntegerLike,
OpTrait::IsCommutative, OpTrait::HasNoSideEffect> {
public:
static StringRef getOperationName() { return "addi"; }
Attribute constantFold(ArrayRef<Attribute> operands,
MLIRContext *context) const;
static void getCanonicalizationPatterns(OwningRewritePatternList &results,
MLIRContext *context);
private:
friend class OperationInst;
explicit AddIOp(const OperationInst *state) : BinaryOp(state) {}
};
#define GET_OP_CLASSES
#include "mlir/StandardOps/standard_ops.inc"
/// The "alloc" operation allocates a region of memory, as specified by its
/// memref type. For example:
@ -650,51 +605,6 @@ private:
explicit MemRefCastOp(const OperationInst *state) : CastOp(state) {}
};
/// The "mulf" operation takes two operands and returns one result, each of
/// these is required to be of the same type. This type may be a floating point
/// scalar type, a vector whose element type is a floating point type, or a
/// floating point tensor. For example:
///
/// %2 = mulf %0, %1 : f32
///
class MulFOp
: public BinaryOp<MulFOp, OpTrait::ResultsAreFloatLike,
OpTrait::IsCommutative, OpTrait::HasNoSideEffect> {
public:
static StringRef getOperationName() { return "mulf"; }
Attribute constantFold(ArrayRef<Attribute> operands,
MLIRContext *context) const;
private:
friend class OperationInst;
explicit MulFOp(const OperationInst *state) : BinaryOp(state) {}
};
/// The "muli" operation takes two operands and returns one result, each of
/// these is required to be of the same type. This type may be an integer
/// scalar type, a vector whose element type is an integer type, or an
/// integer tensor. For example:
///
/// %2 = muli %0, %1 : i32
///
class MulIOp
: public BinaryOp<MulIOp, OpTrait::ResultsAreIntegerLike,
OpTrait::IsCommutative, OpTrait::HasNoSideEffect> {
public:
static StringRef getOperationName() { return "muli"; }
Attribute constantFold(ArrayRef<Attribute> operands,
MLIRContext *context) const;
static void getCanonicalizationPatterns(OwningRewritePatternList &results,
MLIRContext *context);
private:
friend class OperationInst;
explicit MulIOp(const OperationInst *state) : BinaryOp(state) {}
};
/// The "select" operation chooses one value based on a binary condition
/// supplied as its first operand. If the value of the first operand is 1, the
/// second operand is chosen, otherwise the third operand is chosen. The second
@ -784,49 +694,6 @@ private:
explicit StoreOp(const OperationInst *state) : Op(state) {}
};
/// The "subf" operation takes two operands and returns one result, each of
/// these is required to be of the same type. This type may be a floating point
/// scalar type, a vector whose element type is a floating point type, or a
/// floating point tensor. For example:
///
/// %2 = subf %0, %1 : f32
///
class SubFOp : public BinaryOp<SubFOp, OpTrait::ResultsAreFloatLike,
OpTrait::HasNoSideEffect> {
public:
static StringRef getOperationName() { return "subf"; }
Attribute constantFold(ArrayRef<Attribute> operands,
MLIRContext *context) const;
private:
friend class OperationInst;
explicit SubFOp(const OperationInst *state) : BinaryOp(state) {}
};
/// The "subi" operation takes two operands and returns one result, each of
/// these is required to be of the same type. This type may be an integer
/// scalar type, a vector whose element type is an integer type, or a
/// integer tensor. For example:
///
/// %2 = subi %0, %1 : i32
///
class SubIOp : public BinaryOp<SubIOp, OpTrait::ResultsAreIntegerLike,
OpTrait::HasNoSideEffect> {
public:
static StringRef getOperationName() { return "subi"; }
Attribute constantFold(ArrayRef<Attribute> operands,
MLIRContext *context) const;
static void getCanonicalizationPatterns(OwningRewritePatternList &results,
MLIRContext *context);
private:
friend class OperationInst;
explicit SubIOp(const OperationInst *state) : BinaryOp(state) {}
};
/// The "tensor_cast" operation converts a tensor from one type to an equivalent
/// type without changing any data elements. The source and destination types
/// must both be tensor types with the same element type, and the source and

View File

@ -0,0 +1,119 @@
//===- standard_ops.td - Standard operation definitions ----*- tablegen -*-===//
//
// 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.
// =============================================================================
//
// Defines some MLIR standard operations.
//
//===----------------------------------------------------------------------===//
#ifdef STANDARD_OPS
#else
#define STANDARD_OPS
#ifdef OP_BASE
#else
include "mlir/IR/op_base.td"
#endif // OP_BASE
def AnyType : Type;
// Base class for standard arithmetic operations. Requires operands and
// results to be of the same type, but does not constrain them to specific
// types. Individual classes will have `lhs` and `rhs` accessor to operands.
class ArithmeticOp<string mnemonic, list<OpProperty> props = [],
list<string> traits = []> :
Op<mnemonic, !listconcat(props, [NoSideEffect])>,
Traits<!listconcat(traits, ["SameOperandsAndResultType"])>,
Arguments<(ins AnyType:$lhs, AnyType:$rhs)>,
Results<[AnyType]> {
let opName = mnemonic;
let builder = [{
static void build(Builder *builder, OperationState *result, Value *lhs,
Value *rhs) {
impl::buildBinaryOp(builder, result, lhs, rhs);
}
}];
let parser = [{
return impl::parseBinaryOp(parser, result);
}];
let printer = [{
return impl::printBinaryOp(this->getInstruction(), p);
}];
}
// Base class for standard arithmetic operations on integers, vectors and
// tensors thereof. This operation takes two operands and returns one result,
// each of these is required to be of the same type. This type may be an
// integer scalar type, a vector whose element type is an integer type, or an
// integer tensor. The short-hand syntax of the operaton is as follows
//
// <op>i %0, %1 : i32
class IntArithmeticOp<string mnemonic, list<OpProperty> props = [],
list<string> traits = []> :
ArithmeticOp<mnemonic, props,
!listconcat(["ResultsAreIntegerLike"], traits)>;
// Base class for standard arithmetic binary operations on floats, vectors and
// tensors thereof. This operation has two operands and returns one result,
// each of these is required to be of the same type. This type may be a
// floating point scalar type, a vector whose element type is a floating point
// type, or a floating point tensor. The short-hand syntax of the operation is
// as follows
//
// <op>f %0, %1 : f32
class FloatArithmeticOp<string mnemonic, list<OpProperty> props = [],
list<string> traits = []> :
ArithmeticOp<mnemonic, props,
!listconcat(["ResultsAreFloatLike"], traits)>;
def AddFOp : FloatArithmeticOp<"addf"> {
let summary = "floating point addition operation";
let hasConstantFolder = 0b1;
}
def AddIOp : IntArithmeticOp<"addi", [Commutative]> {
let summary = "integer addition operation";
let hasCanonicalizationPatterns = 0b1;
let hasConstantFolder = 0b1;
}
def MulFOp : FloatArithmeticOp<"mulf"> {
let summary = "foating point multiplication operation";
let hasConstantFolder = 0b1;
}
def MulIOp : IntArithmeticOp<"muli", [Commutative]> {
let summary = "integer multiplication operation";
let hasCanonicalizationPatterns = 0b1;
let hasConstantFolder = 0b1;
}
def SubFOp : FloatArithmeticOp<"subf"> {
let summary = "floating point subtraction operation";
let hasConstantFolder = 0b1;
}
def SubIOp : IntArithmeticOp<"subi"> {
let summary = "integer subtraction operation";
let hasConstantFolder = 0b1;
let hasCanonicalizationPatterns = 0b1;
}
#endif // STANDARD_OPS

View File

@ -37,10 +37,12 @@ using namespace mlir;
StandardOpsDialect::StandardOpsDialect(MLIRContext *context)
: Dialect(/*namePrefix=*/"", context) {
addOperations<AddFOp, AddIOp, AllocOp, CallOp, CallIndirectOp, CmpIOp,
DeallocOp, DimOp, DmaStartOp, DmaWaitOp, ExtractElementOp,
LoadOp, MemRefCastOp, MulFOp, MulIOp, SelectOp, StoreOp, SubFOp,
SubIOp, TensorCastOp>();
addOperations<AllocOp, CallOp, CallIndirectOp, CmpIOp, DeallocOp, DimOp,
DmaStartOp, DmaWaitOp, ExtractElementOp, LoadOp, MemRefCastOp,
SelectOp, StoreOp, TensorCastOp,
#define GET_OP_LIST
#include "mlir/StandardOps/standard_ops.inc"
>();
}
//===----------------------------------------------------------------------===//
@ -78,13 +80,6 @@ struct MemRefCastFolder : public RewritePattern {
// AddFOp
//===----------------------------------------------------------------------===//
void AddFOp::build(Builder *builder, OperationState *result, Value *lhs,
Value *rhs) {
assert(lhs->getType() == rhs->getType());
result->addOperands({lhs, rhs});
result->types.push_back(lhs->getType());
}
Attribute AddFOp::constantFold(ArrayRef<Attribute> operands,
MLIRContext *context) const {
assert(operands.size() == 2 && "addf takes two operands");

View File

@ -113,6 +113,9 @@ public:
// Emit method declaration for the getCanonicalizationPatterns() interface.
void emitCanonicalizationPatterns();
// Emit the constant folder method for the operation.
void emitConstantFolder();
// Emit the parser for the operation.
void emitParser();
@ -171,6 +174,7 @@ void OpEmitter::emit(const Record &def, raw_ostream &os) {
emitter.emitVerifier();
emitter.emitAttrGetters();
emitter.emitCanonicalizationPatterns();
emitter.emitConstantFolder();
os << "private:\n friend class ::mlir::OperationInst;\n";
os << " explicit " << emitter.op.cppClassName()
@ -321,6 +325,19 @@ void OpEmitter::emitCanonicalizationPatterns() {
<< "OwningRewritePatternList &results, MLIRContext* context);\n";
}
void OpEmitter::emitConstantFolder() {
if (!def.getValueAsBit("hasConstantFolder"))
return;
if (def.getValueAsListOfDefs("returnTypes").size() == 1) {
os << " Attribute constantFold(ArrayRef<Attribute> operands,\n"
" MLIRContext *context) const;\n";
} else {
os << " bool constantFold(ArrayRef<Attribute> operands,\n"
<< " SmallVectorImpl<Attribute> &results,\n"
<< " MLIRContext *context) const;\n";
}
}
void OpEmitter::emitParser() {
if (!hasStringAttribute(def, "parser"))
return;