From 967b93f6252902255de937fb13cc304b978fc779 Mon Sep 17 00:00:00 2001 From: Morten Borup Petersen Date: Thu, 11 May 2023 09:50:23 +0200 Subject: [PATCH] [DC] Add ops and types (#5157) --- include/circt/Dialect/DC/DC.td | 2 + include/circt/Dialect/DC/DCDialect.td | 6 + include/circt/Dialect/DC/DCOps.h | 36 +++++ include/circt/Dialect/DC/DCOps.td | 224 ++++++++++++++++++++++++++ include/circt/Dialect/DC/DCTypes.h | 18 +++ include/circt/Dialect/DC/DCTypes.td | 42 +++++ lib/Dialect/DC/CMakeLists.txt | 2 + lib/Dialect/DC/DCDialect.cpp | 4 +- lib/Dialect/DC/DCOps.cpp | 195 ++++++++++++++++++++++ lib/Dialect/DC/DCTypes.cpp | 27 ++++ test/Dialect/DC/errors.mlir | 13 ++ test/Dialect/DC/roundtrip.mlir | 20 +++ 12 files changed, 588 insertions(+), 1 deletion(-) create mode 100644 include/circt/Dialect/DC/DCOps.h create mode 100644 include/circt/Dialect/DC/DCOps.td create mode 100644 include/circt/Dialect/DC/DCTypes.h create mode 100644 include/circt/Dialect/DC/DCTypes.td create mode 100644 lib/Dialect/DC/DCOps.cpp create mode 100644 lib/Dialect/DC/DCTypes.cpp create mode 100644 test/Dialect/DC/errors.mlir create mode 100644 test/Dialect/DC/roundtrip.mlir diff --git a/include/circt/Dialect/DC/DC.td b/include/circt/Dialect/DC/DC.td index dac132b7af78..2e3435ccd79f 100644 --- a/include/circt/Dialect/DC/DC.td +++ b/include/circt/Dialect/DC/DC.td @@ -12,5 +12,7 @@ include "mlir/IR/OpBase.td" include "circt/Dialect/DC/DCDialect.td" +include "circt/Dialect/DC/DCTypes.td" +include "circt/Dialect/DC/DCOps.td" #endif // CIRCT_DIALECT_DC_DC_TD diff --git a/include/circt/Dialect/DC/DCDialect.td b/include/circt/Dialect/DC/DCDialect.td index fceed9e8b0b7..d7914a8090cc 100644 --- a/include/circt/Dialect/DC/DCDialect.td +++ b/include/circt/Dialect/DC/DCDialect.td @@ -19,6 +19,12 @@ def DCDialect : Dialect { handshaking semantics. }]; let cppNamespace = "circt::dc"; + + let useDefaultTypePrinterParser = 1; + + let extraClassDeclaration = [{ + void registerTypes(); + }]; } #endif // CIRCT_DIALECT_DC_DIALECT_TD diff --git a/include/circt/Dialect/DC/DCOps.h b/include/circt/Dialect/DC/DCOps.h new file mode 100644 index 000000000000..d3ba04be1c10 --- /dev/null +++ b/include/circt/Dialect/DC/DCOps.h @@ -0,0 +1,36 @@ +//===- DCOps.h - DC dialect operations --------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_DC_DCOPS_H +#define CIRCT_DIALECT_DC_DCOPS_H + +#include "mlir/IR/FunctionInterfaces.h" +#include "mlir/IR/OpImplementation.h" +#include "mlir/IR/RegionKindInterface.h" +#include "mlir/IR/SymbolTable.h" +#include "mlir/Interfaces/CallInterfaces.h" +#include "mlir/Interfaces/ControlFlowInterfaces.h" +#include "mlir/Interfaces/InferTypeOpInterface.h" +#include "mlir/Interfaces/SideEffectInterfaces.h" + +#include "circt/Dialect/DC/DCDialect.h" +#include "circt/Dialect/DC/DCTypes.h" + +namespace circt { +namespace dc { + +// Returns true if 't'is a `dc.value` type. +bool isI1ValueType(Type t); + +} // namespace dc +} // namespace circt + +#define GET_OP_CLASSES +#include "circt/Dialect/DC/DC.h.inc" + +#endif // CIRCT_DIALECT_DC_DCOPS_H diff --git a/include/circt/Dialect/DC/DCOps.td b/include/circt/Dialect/DC/DCOps.td new file mode 100644 index 000000000000..eaee72ea2c56 --- /dev/null +++ b/include/circt/Dialect/DC/DCOps.td @@ -0,0 +1,224 @@ +//===- DCOps.td - DC dialect operations --------------------*- tablegen -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_DC_OPS_TD +#define CIRCT_DIALECT_DC_OPS_TD + +include "mlir/Interfaces/InferTypeOpInterface.td" +include "circt/Dialect/DC/DCDialect.td" +include "circt/Dialect/DC/DCTypes.td" +include "mlir/IR/FunctionInterfaces.td" +include "mlir/IR/OpAsmInterface.td" +include "mlir/IR/RegionKindInterface.td" +include "mlir/IR/SymbolInterfaces.td" +include "mlir/Interfaces/CallInterfaces.td" +include "mlir/Interfaces/ControlFlowInterfaces.td" +include "mlir/Interfaces/SideEffectInterfaces.td" +include "mlir/IR/BuiltinAttributes.td" +include "mlir/IR/FunctionInterfaces.td" + +class DCOp traits = []> : + Op; + +class SameTypeConstraint + : TypesMatchWith<"lhs and rhs types should be equivalent", + lhs, rhs, [{ $_self }]>; + +def BufferOp : DCOp<"buffer", + [SameTypeConstraint<"input", "output">]> { + let summary = "Buffer operation"; + let description = [{ + The buffer operation may buffer a `dc.value` or `dc.token` typed SSA value. + In practice, this provides a mechanism to buffer data-side values in a + control-sensitive manner. + + Example: + ```mlir + %value_out = dc.buffer [2] %value : !dc.value + ``` + }]; + + let arguments = (ins + ValueOrTokenType:$input, + ConfinedAttr]>:$size, + OptionalAttr:$initValues + ); + let results = (outs ValueOrTokenType:$output); + + let assemblyFormat = "`[` $size `]` $input ($initValues^)? attr-dict `:` type($input)"; + let hasVerifier = 1; + let builders = [OpBuilder<( + ins "Value":$input, "size_t":$size), [{ + build($_builder, $_state, input.getType(), input, $_builder.getI64IntegerAttr(size), {}); + }]>]; + + let extraClassDeclaration = [{ + // Returns the data type of this buffer, if any. + std::optional getInnerTypes() { + if(auto type = getInput().getType().dyn_cast()) + return type.getInnerTypes(); + return std::nullopt; + } + + // Returns the initial values of this buffer as a vector of int64's. + FailureOr> getInitValueArray(); + }]; +} + +def JoinOp : DCOp<"join"> { + let summary = "Synchronizes the incoming tokens with the outgoing token"; + let description = [{ + This operator synchronizes all incoming tokens. Synchronization implies applying + join semantics in between all in- and output ports. + + Example: + ```mlir + %0 = dc.join %a, %b + ``` + }]; + + let arguments = (ins Variadic:$tokens); + let results = (outs TokenType:$output); + + let assemblyFormat = "$tokens attr-dict"; + let hasCanonicalizer = 1; + let builders = [OpBuilder<( + ins "ValueRange":$tokens), [{ + build($_builder, $_state, $_builder.getType(), tokens); + }]>]; +} + +def ForkOp : DCOp<"fork"> { + let summary = "Splits the incoming token into multiple outgoing tokens"; + let description = [{ + This operator splits the incoming token into multiple outgoing tokens. + + Example: + ```mlir + %0, %1 = dc.fork [2] %a : !dc.token, !dc.token + ``` + }]; + + let arguments = (ins TokenType:$token); + let results = (outs Variadic:$outputs); + let hasCustomAssemblyFormat = 1; + let hasCanonicalizer = 1; + + let builders = [OpBuilder<( + ins "Value":$token, "size_t":$numOutputs), [{ + llvm::SmallVector outputTypes(numOutputs, $_builder.getType()); + build($_builder, $_state, outputTypes, token); + }]>]; +} + +def BranchOp : DCOp<"branch"> { + let summary = "Branch operation"; + let description = [{ + The incoming select token is propagated to the selected output based on + the value of the condition. + }]; + + let arguments = (ins I1ValueType:$condition); + let results = (outs TokenType:$trueToken, TokenType:$falseToken); + + let assemblyFormat = "$condition attr-dict"; +} + +def SelectOp : DCOp<"select"> { + let summary = "Select operation"; + let description = [{ + An input token is selected based on the value of the incoming select + signal, and propagated to the single output. Only the condition value, + the selected input, and the output will be transacted. + }]; + + let arguments = (ins I1ValueType:$condition, TokenType:$trueToken, TokenType:$falseToken); + let results = (outs TokenType:$output); + let assemblyFormat = "$condition `,` $trueToken `,` $falseToken attr-dict"; + let hasCanonicalizer = 1; +} + +def SinkOp : DCOp<"sink"> { + let summary = "Sink operation"; + let description = [{ + The sink operation will always accept any incoming tokens, and + discard them. + }]; + + let arguments = (ins TokenType:$token); + let results = (outs); + let assemblyFormat = "$token attr-dict"; +} + +def SourceOp : DCOp<"source"> { + let summary = "Source operation"; + let description = [{ + The source operation will always produce a token. + }]; + + let arguments = (ins); + let results = (outs TokenType:$output); + let assemblyFormat = "attr-dict"; + let builders = [OpBuilder<(ins), [{ + build($_builder, $_state, $_builder.getType()); + }]>]; +} + +def PackOp : DCOp<"pack", [InferTypeOpInterface]> { + let summary = "Pack operation"; + let description = [{ + An operation which packs together a !dc.token value with some other + value. + }]; + + let arguments = (ins TokenType:$token, Variadic:$inputs); + let results = (outs ValueType:$output); + let assemblyFormat = "$token `[` $inputs `]` attr-dict `:` type($inputs)"; + let hasCanonicalizer = 1; + let hasFolder = 1; + + let extraClassDeclaration = [{ + /// Infer the return types of this operation. + static LogicalResult inferReturnTypes(MLIRContext *context, + std::optional loc, + ValueRange operands, + DictionaryAttr attrs, + mlir::OpaqueProperties properties, + mlir::RegionRange regions, + SmallVectorImpl &results); + }]; +} + +def UnpackOp : DCOp<"unpack", [InferTypeOpInterface]> { + let summary = "Unpack operation"; + let description = [{ + An operation which unpacks a !dc.value value into a !dc.token value + and its constituent values. + }]; + + let arguments = (ins ValueType:$input); + let results = (outs TokenType:$token, Variadic:$outputs); + let assemblyFormat = "$input attr-dict `:` type($input)"; + let hasCanonicalizer = 1; + let hasFolder = 1; + + let extraClassDeclaration = [{ + /// Infer the return types of this operation. + static LogicalResult inferReturnTypes(MLIRContext *context, + std::optional loc, + ValueRange operands, + DictionaryAttr attrs, + mlir::OpaqueProperties properties, + mlir::RegionRange regions, + SmallVectorImpl &results); + }]; +} + +#endif // CIRCT_DIALECT_DC_OPS_TD diff --git a/include/circt/Dialect/DC/DCTypes.h b/include/circt/Dialect/DC/DCTypes.h new file mode 100644 index 000000000000..7314c65d20cc --- /dev/null +++ b/include/circt/Dialect/DC/DCTypes.h @@ -0,0 +1,18 @@ +//===- DCTypes.h - DC dialect types -----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_DC_DCTYPES_H +#define CIRCT_DIALECT_DC_DCTYPES_H + +#include "mlir/IR/BuiltinTypes.h" +#include "mlir/IR/Types.h" + +#define GET_TYPEDEF_CLASSES +#include "circt/Dialect/DC/DCTypes.h.inc" + +#endif // CIRCT_DIALECT_DC_DCTYPES_H diff --git a/include/circt/Dialect/DC/DCTypes.td b/include/circt/Dialect/DC/DCTypes.td new file mode 100644 index 000000000000..cdda8d4051c5 --- /dev/null +++ b/include/circt/Dialect/DC/DCTypes.td @@ -0,0 +1,42 @@ +//===- DCTypes.td - DC dialect types -----------------------*- tablegen -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_DC_TYPES_TD +#define CIRCT_DIALECT_DC_TYPES_TD + +include "circt/Dialect/DC/DCDialect.td" +include "mlir/IR/AttrTypeBase.td" + +class DCTypeDef : TypeDef { } + +def TokenType : DCTypeDef<"Token"> { + let mnemonic = "token"; + let parameters = (ins ); + let assemblyFormat = ""; +} + +// A value type is a set of values which is wrapped with token semantics. +def ValueType : DCTypeDef<"Value"> { + let mnemonic = "value"; + let parameters = (ins ArrayRefParameter<"Type">:$innerTypes); + let assemblyFormat = "`<`$innerTypes`>`"; +} + +def I1ValueType : Type< + CPred<"circt::dc::isI1ValueType($_self)">, "must be a !dc.value type">, + BuildableType<"$_builder.getType($_builder.getI1Type())"> { +} + +def ValueOrTokenType : Type< + CPred<"$_self.isa()">, + "must be a !dc.value or !dc.token type"> { +} + + + +#endif // CIRCT_DIALECT_DC_TYPES_TD diff --git a/lib/Dialect/DC/CMakeLists.txt b/lib/Dialect/DC/CMakeLists.txt index 5c2aaf0a7b6e..4bb3137ae0d2 100644 --- a/lib/Dialect/DC/CMakeLists.txt +++ b/lib/Dialect/DC/CMakeLists.txt @@ -1,5 +1,7 @@ add_circt_dialect_library(CIRCTDC DCDialect.cpp + DCOps.cpp + DCTypes.cpp ADDITIONAL_HEADER_DIRS ${CIRCT_MAIN_INCLUDE_DIR}/circt/Dialect/DC diff --git a/lib/Dialect/DC/DCDialect.cpp b/lib/Dialect/DC/DCDialect.cpp index 31c527a5ddec..fa954d6f971f 100644 --- a/lib/Dialect/DC/DCDialect.cpp +++ b/lib/Dialect/DC/DCDialect.cpp @@ -7,12 +7,14 @@ //===----------------------------------------------------------------------===// #include "circt/Dialect/DC/DCDialect.h" -#include "mlir/Transforms/InliningUtils.h" +#include "circt/Dialect/DC/DCOps.h" +#include "circt/Dialect/DC/DCTypes.h" using namespace circt; using namespace dc; void DCDialect::initialize() { + registerTypes(); addOperations< #define GET_OP_LIST #include "circt/Dialect/DC/DC.cpp.inc" diff --git a/lib/Dialect/DC/DCOps.cpp b/lib/Dialect/DC/DCOps.cpp new file mode 100644 index 000000000000..48feafad867f --- /dev/null +++ b/lib/Dialect/DC/DCOps.cpp @@ -0,0 +1,195 @@ +//===- DCOps.cpp ----------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "circt/Dialect/DC/DCOps.h" +#include "mlir/IR/Builders.h" +#include "mlir/IR/FunctionImplementation.h" +#include "mlir/IR/OpImplementation.h" +#include "mlir/IR/PatternMatch.h" +#include "mlir/Interfaces/SideEffectInterfaces.h" + +using namespace circt; +using namespace dc; +using namespace mlir; + +bool circt::dc::isI1ValueType(Type t) { + auto vt = t.dyn_cast(); + if (!vt || vt.getInnerTypes().size() != 1) + return false; + + auto innerWidth = vt.getInnerTypes()[0].getIntOrFloatBitWidth(); + return innerWidth == 1; +} + +namespace circt { +namespace dc { +#include "circt/Dialect/DC/DCCanonicalization.h.inc" + +// ============================================================================= +// JoinOp +// ============================================================================= + +void JoinOp::getCanonicalizationPatterns(RewritePatternSet &results, + MLIRContext *context) {} + +// ============================================================================= +// ForkOp +// ============================================================================= + +template +static ParseResult parseIntInSquareBrackets(OpAsmParser &parser, TInt &v) { + if (parser.parseLSquare() || parser.parseInteger(v) || parser.parseRSquare()) + return failure(); + return success(); +} + +ParseResult ForkOp::parse(OpAsmParser &parser, OperationState &result) { + OpAsmParser::UnresolvedOperand operand; + size_t size = 0; + if (parseIntInSquareBrackets(parser, size)) + return failure(); + + if (size == 0) + return parser.emitError(parser.getNameLoc(), + "fork size must be greater than 0"); + + if (parser.parseOperand(operand) || + parser.parseOptionalAttrDict(result.attributes)) + return failure(); + + auto tt = dc::TokenType::get(parser.getContext()); + llvm::SmallVector operandTypes{tt}; + SmallVector resultTypes{size, tt}; + result.addTypes(resultTypes); + if (parser.resolveOperand(operand, tt, result.operands)) + return failure(); + return success(); +} + +void ForkOp::print(OpAsmPrinter &p) { + p << "[" << getNumResults() << "] "; + p << getOperand() << " "; + auto attrs = (*this)->getAttrs(); + if (!attrs.empty()) { + p << " "; + p.printOptionalAttrDict(attrs); + } +} + +void ForkOp::getCanonicalizationPatterns(RewritePatternSet &results, + MLIRContext *context) {} + +// ============================================================================= +// UnpackOp +// ============================================================================= + +void UnpackOp::getCanonicalizationPatterns(RewritePatternSet &results, + MLIRContext *context) {} + +LogicalResult UnpackOp::fold(FoldAdaptor adaptor, + SmallVectorImpl &results) { + // Unpack of a pack is a no-op. + if (auto pack = getInput().getDefiningOp()) { + results.push_back(pack.getToken()); + results.append(pack.getInputs().begin(), pack.getInputs().end()); + return success(); + } + + return failure(); +} + +LogicalResult UnpackOp::inferReturnTypes( + MLIRContext *context, std::optional loc, ValueRange operands, + DictionaryAttr attrs, mlir::OpaqueProperties properties, + mlir::RegionRange regions, SmallVectorImpl &results) { + auto inputType = operands.front().getType().cast(); + results.push_back(TokenType::get(context)); + results.append(inputType.getInnerTypes().begin(), + inputType.getInnerTypes().end()); + return success(); +} + +// ============================================================================= +// PackOp +// ============================================================================= + +void PackOp::getCanonicalizationPatterns(RewritePatternSet &results, + MLIRContext *context) {} + +OpFoldResult PackOp::fold(FoldAdaptor adaptor) { + auto token = getToken(); + auto inputs = getInputs(); + + // Pack of an unpack is a no-op. + if (auto unpack = token.getDefiningOp()) { + llvm::SmallVector unpackResults = unpack.getResults(); + if (unpackResults.size() == inputs.size() && + llvm::all_of(llvm::zip(getInputs(), unpackResults), [&](auto it) { + return std::get<0>(it) == std::get<1>(it); + })) { + return unpack.getInput(); + } + } + + return {}; +} + +LogicalResult PackOp::inferReturnTypes( + MLIRContext *context, std::optional loc, ValueRange operands, + DictionaryAttr attrs, mlir::OpaqueProperties properties, + mlir::RegionRange regions, SmallVectorImpl &results) { + llvm::SmallVector inputTypes; + for (auto t : operands.drop_front().getTypes()) + inputTypes.push_back(t); + auto valueType = dc::ValueType::get(context, inputTypes); + results.push_back(valueType); + return success(); +} + +// ============================================================================= +// SelectOp +// ============================================================================= + +void SelectOp::getCanonicalizationPatterns(RewritePatternSet &results, + MLIRContext *context) {} + +// ============================================================================= +// BufferOp +// ============================================================================= + +FailureOr> BufferOp::getInitValueArray() { + assert(getInitValues() && "initValues attribute not set"); + SmallVector values; + for (auto value : getInitValuesAttr()) { + if (auto iValue = value.dyn_cast()) { + values.push_back(iValue.getValue().getSExtValue()); + } else { + return emitError() << "initValues attribute must be an array of integers"; + } + } + return values; +} + +LogicalResult BufferOp::verify() { + // Verify that exactly 'size' number of initial values have been provided, if + // an initializer list have been provided. + if (auto initVals = getInitValuesAttr()) { + auto nInits = initVals.size(); + if (nInits != getSize()) + return emitOpError() << "expected " << getSize() + << " init values but got " << nInits << "."; + } + + return success(); +} + +} // namespace dc +} // namespace circt + +#define GET_OP_CLASSES +#include "circt/Dialect/DC/DC.cpp.inc" diff --git a/lib/Dialect/DC/DCTypes.cpp b/lib/Dialect/DC/DCTypes.cpp new file mode 100644 index 000000000000..728a5d225285 --- /dev/null +++ b/lib/Dialect/DC/DCTypes.cpp @@ -0,0 +1,27 @@ +//===- DCTypes.cpp --------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "circt/Dialect/DC/DCTypes.h" +#include "circt/Dialect/DC/DCDialect.h" +#include "mlir/IR/Builders.h" +#include "mlir/IR/DialectImplementation.h" +#include "llvm/ADT/TypeSwitch.h" + +using namespace circt; +using namespace dc; +using namespace mlir; + +#define GET_TYPEDEF_CLASSES +#include "circt/Dialect/DC/DCTypes.cpp.inc" + +void DCDialect::registerTypes() { + addTypes< +#define GET_TYPEDEF_LIST +#include "circt/Dialect/DC/DCTypes.cpp.inc" + >(); +} diff --git a/test/Dialect/DC/errors.mlir b/test/Dialect/DC/errors.mlir new file mode 100644 index 000000000000..b37f03a8efb1 --- /dev/null +++ b/test/Dialect/DC/errors.mlir @@ -0,0 +1,13 @@ +// RUN: circt-opt %s -split-input-file -verify-diagnostics + +hw.module @buffer(%0 : !dc.value) { + // expected-error @+1 {{'dc.buffer' op expected 2 init values but got 1.}} + %bufferInit = dc.buffer [2] %0 [1] : !dc.value +} + +// ----- + +hw.module @types(%0 : i1) { + // expected-error @+1 {{custom op 'dc.buffer' 'input' must be must be a !dc.value or !dc.token type, but got 'i1'}} + %bufferInit = dc.buffer [2] %0 : i1 +} diff --git a/test/Dialect/DC/roundtrip.mlir b/test/Dialect/DC/roundtrip.mlir new file mode 100644 index 000000000000..08f787e55b4d --- /dev/null +++ b/test/Dialect/DC/roundtrip.mlir @@ -0,0 +1,20 @@ +// RUN: circt-opt %s | circt-opt | FileCheck %s + +// CHECK-LABEL: hw.module @foo( +// CHECK-SAME: %[[VAL_0:.*]]: !dc.token, +// CHECK-SAME: %[[VAL_1:.*]]: !dc.value, +// CHECK-SAME: %[[VAL_2:.*]]: i32) attributes {argNames = ["", "", ""]} { +// CHECK: %[[VAL_3:.*]] = dc.buffer[2] %[[VAL_0]] : !dc.token +// CHECK: %[[VAL_4:.*]] = dc.buffer[2] %[[VAL_1]] [1, 2] : !dc.value +// CHECK: %[[VAL_5:.*]]:2 = dc.fork[2] %[[VAL_0]] +// CHECK: %[[VAL_6:.*]] = dc.pack %[[VAL_0]]{{\[}}%[[VAL_2]]] : i32 +// CHECK: %[[VAL_7:.*]], %[[VAL_8:.*]] = dc.unpack %[[VAL_1]] : +// CHECK: hw.output +// CHECK: } +hw.module @foo(%0 : !dc.token, %1 : !dc.value, %2 : i32) { + %buffer = dc.buffer [2] %0 : !dc.token + %bufferInit = dc.buffer [2] %1 [1, 2] : !dc.value + %f1, %f2 = dc.fork [2] %0 + %pack = dc.pack %0 [%2] : i32 + %unpack_token, %unpack_value = dc.unpack %1 : !dc.value +} \ No newline at end of file