mirror of https://github.com/llvm/circt.git
[Handshake] Add tuple pack and unpack operations
This commit is contained in:
parent
096c5db2f9
commit
da0c16a8ac
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue