[Handshake] Add tuple pack and unpack operations

This commit is contained in:
Christian Ulmann 2022-05-04 15:09:00 +02:00
parent 096c5db2f9
commit da0c16a8ac
4 changed files with 190 additions and 0 deletions

View File

@ -12,6 +12,7 @@
include "mlir/IR/EnumAttr.td"
include "mlir/IR/OpAsmInterface.td"
include "mlir/IR/BuiltinTypes.td"
// This is almost exactly like a standard FuncOp, except that it has some
// extra verification conditions. In particular, each Value must
@ -812,3 +813,67 @@ def JoinOp : Handshake_Op<"join", [
def I4 : I<4>;
def I4Attr : SignlessIntegerAttrBase<I4, "4-bit integer attribute">;
// TODO(Dinistro): add support for this in the handshake-runner
def UnpackOp : Handshake_Op<"unpack", [
TypesMatchWith<"result types match element types of 'tuple'",
"input", "results",
"$_self.cast<TupleType>().getTypes()">
]> {
let summary = "unpacks a tuple";
let description = [{
The `unpack` operation assigns each value of a tuple to a separate
value for further processing. The number of results corresponds
to the number of tuple elements.
Similar to `fork`, each output is distributed as soon as the corresponding
successor is ready.
Example:
```mlir
%a, %b = handshake.unpack %tuple {attributes} : tuple<i32, i64>
```
}];
let arguments = (ins Builtin_Tuple:$input);
let results = (outs Variadic<AnyType>:$results);
let builders = [OpBuilder<(ins "Value":$input), [{
$_state.addOperands(input);
TupleType type = input.getType().dyn_cast_or_null<TupleType>();
assert(type && "expect unpack to have a 'TupleType' operand");
$_state.addTypes(type.getTypes());
}]>];
let hasCustomAssemblyFormat = 1;
}
def PackOp : Handshake_Op<"pack", [
TypesMatchWith<"input types match element types of 'tuple'",
"result", "inputs",
"$_self.cast<TupleType>().getTypes()">
]> {
let summary = "packs a tuple";
let description = [{
The `pack` operation constructs a tuple from separate values.
The number of operands corresponds to the number of tuple elements.
Similar to `join`, the output is ready when all inputs are ready.
Example:
```mlir
%tuple = handshake.pack %a, %b {attributes} : tuple<i32, i64>
```
}];
let arguments = (ins Variadic<AnyType>:$inputs);
let results = (outs Builtin_Tuple:$result);
let builders = [OpBuilder<
(ins "ValueRange":$operands), [{
$_state.addOperands(operands);
$_state.addTypes(TupleType::get($_builder.getContext(), operands.getTypes()));
}]>];
let hasCustomAssemblyFormat = 1;
}

View File

@ -1445,6 +1445,54 @@ LogicalResult InstanceOp::verify() {
return success();
}
ParseResult UnpackOp::parse(OpAsmParser &parser, OperationState &result) {
OpAsmParser::UnresolvedOperand tuple;
TupleType type;
if (parser.parseOperand(tuple) ||
parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
parser.parseType(type))
return failure();
if (parser.resolveOperand(tuple, type, result.operands))
return failure();
result.addTypes(type.getTypes());
return success();
}
void UnpackOp::print(OpAsmPrinter &p) {
p << " " << input();
p.printOptionalAttrDict((*this)->getAttrs());
p << " : " << input().getType();
}
ParseResult PackOp::parse(OpAsmParser &parser, OperationState &result) {
SmallVector<OpAsmParser::UnresolvedOperand, 4> operands;
llvm::SMLoc allOperandLoc = parser.getCurrentLocation();
TupleType type;
if (parser.parseOperandList(operands) ||
parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
parser.parseType(type))
return failure();
if (parser.resolveOperands(operands, type.getTypes(), allOperandLoc,
result.operands))
return failure();
result.addTypes(type);
return success();
}
void PackOp::print(OpAsmPrinter &p) {
p << " " << inputs();
p.printOptionalAttrDict((*this)->getAttrs());
p << " : " << result().getType();
}
//===----------------------------------------------------------------------===//
// TableGen'd op method definitions
//===----------------------------------------------------------------------===//

View File

@ -132,3 +132,44 @@ handshake.func @invalid_buffer_init4(%arg0 : i32, %ctrl : none) -> (i32, none) {
%0 = buffer [1] SEQ %arg0 {initValues = [1]} : i32
return %0, %ctrl : i32, none
}
// -----
handshake.func @invalid_unpack_types_not_matching(%arg0 : tuple<i64, i32>, %ctrl : none) -> (i64, none) {
// expected-error @+1 {{'handshake.unpack' op failed to verify that result types match element types of 'tuple'}}
%0:2 = "handshake.unpack"(%arg0) : (tuple<i64, i32>) -> (i64, i64)
return %0#0, %ctrl : i64, none
}
// -----
handshake.func @invalid_pack_types_not_matching(%arg0 : i32, %arg1 : i64, %ctrl : none) -> (tuple<i64, i32>, none) {
// expected-error @+1 {{'handshake.pack' op failed to verify that input types match element types of 'tuple'}}
%0 = "handshake.pack"(%arg0, %arg1) : (i32, i64) -> tuple<i64, i32>
return %0#0, %ctrl : tuple<i64, i32>, none
}
// -----
handshake.func @invalid_unpack_type(%arg0 : i64, %ctrl : none) -> (i64, none) {
// expected-note @-1 {{prior use here}}
// expected-error @+1 {{use of value '%arg0' expects different type than prior uses: 'tuple<i64>' vs 'i64'}}
%0 = handshake.unpack %arg0 : tuple<i64>
return %0, %ctrl : i64, none
}
// -----
handshake.func @invalid_unpack_wrong_types(%arg0 : tuple<i64>, %ctrl : none) -> (i64, none) {
// expected-error @+1 {{'handshake.unpack' invalid kind of type specified}}
%0 = handshake.unpack %arg0 : i64
return %0, %ctrl : i64, none
}
// -----
handshake.func @invalid_pack_wrong_types(%arg0 : i64, %arg1 : i32, %ctrl : none) -> (tuple<i64, i32>, none) {
// expected-error @+1 {{'handshake.pack' invalid kind of type specified}}
%0 = handshake.pack %arg0, %arg1 : i64, i32
return %0, %ctrl : tuple<i64, i32>, none
}

View File

@ -0,0 +1,36 @@
// RUN: circt-opt -split-input-file %s | circt-opt | FileCheck %s
// CHECK-LABEL: handshake.func @unpack_pack(
// CHECK-SAME: %[[VAL_0:.*]]: tuple<i64, i32, i64>,
// CHECK-SAME: %[[VAL_1:.*]]: none, ...) -> (tuple<i32, i64>, none) attributes {argNames = ["in0", "inCtrl"], resNames = ["out0", "outCtrl"]} {
// CHECK: %[[VAL_2:.*]]:3 = unpack %[[VAL_0]] : tuple<i64, i32, i64>
// CHECK: %[[VAL_3:.*]] = arith.addi %[[VAL_2]]#0, %[[VAL_2]]#2 : i64
// CHECK: %[[VAL_4:.*]] = pack %[[VAL_2]]#1, %[[VAL_3]] : tuple<i32, i64>
// CHECK: return %[[VAL_4]], %[[VAL_1]] : tuple<i32, i64>, none
// CHECK:}
handshake.func @unpack_pack(%in: tuple<i64, i32, i64>, %arg1: none, ...) -> (tuple<i32, i64>, none) attributes {argNames = ["in0", "inCtrl"], resNames = ["out0", "outCtrl"]} {
%a, %b, %c = handshake.unpack %in : tuple<i64,i32,i64>
%sum = arith.addi %a, %c : i64
%res = handshake.pack %b, %sum : tuple<i32, i64>
return %res, %arg1 : tuple<i32, i64>, none
}
// -----
// CHECK-LABEL: handshake.func @with_attributes(
// CHECK-SAME: %[[VAL_0:.*]]: tuple<i64, i32>,
// CHECK-SAME: %[[VAL_1:.*]]: none, ...) -> (tuple<i64, i32>, none) attributes {argNames = ["in0", "inCtrl"], resNames = ["out0", "outCtrl"]} {
// CHECK: %[[VAL_2:.*]]:2 = unpack %[[VAL_0]] {testAttr = "content"} : tuple<i64, i32>
// CHECK: %[[VAL_3:.*]] = pack %[[VAL_2]]#0, %[[VAL_2]]#1 {testAttr2 = "content2", testAttr3 = "content3"} : tuple<i64, i32>
// CHECK: return %[[VAL_3]], %[[VAL_1]] : tuple<i64, i32>, none
// CHECK:}
handshake.func @with_attributes(%in: tuple<i64, i32>, %arg1: none, ...) -> (tuple<i64, i32>, none) attributes {argNames = ["in0", "inCtrl"], resNames = ["out0", "outCtrl"]} {
%a, %b = handshake.unpack %in {testAttr = "content"} : tuple<i64,i32>
%res = handshake.pack %a, %b {testAttr2 = "content2", testAttr3 = "content3"} : tuple<i64, i32>
return %res, %arg1 : tuple<i64, i32>, none
}