diff --git a/xls/contrib/mlir/IR/xls_ops.cc b/xls/contrib/mlir/IR/xls_ops.cc index dc102f9787..dc93b489e6 100644 --- a/xls/contrib/mlir/IR/xls_ops.cc +++ b/xls/contrib/mlir/IR/xls_ops.cc @@ -842,6 +842,12 @@ LogicalResult SprocOp::verify() { } } + if (getIsTop() && !getChannelArguments().empty()) { + if (!getBoundaryChannels().has_value()) { + return emitOpError() << "top sprocs must have boundary channels"; + } + } + return success(); } diff --git a/xls/contrib/mlir/IR/xls_ops.td b/xls/contrib/mlir/IR/xls_ops.td index aefc65f830..c6729e045e 100644 --- a/xls/contrib/mlir/IR/xls_ops.td +++ b/xls/contrib/mlir/IR/xls_ops.td @@ -203,6 +203,24 @@ def Xls_FifoConfig : Xls_Attr<"FifoConfig"> { let assemblyFormat = "`<` struct(params) `>`"; } +def Xls_BoundaryChannelAttr : Xls_Attr<"BoundaryChannelAttr"> { + let summary = "Boundary channel attribute"; + let description = [{ + Contains attributes associated with boundary channels for SprocOps. + }]; + let parameters = (ins + "::mlir::StringAttr":$name, + OptionalParameter<"FifoConfig", "">:$fifo_config, + OptionalParameter<"FlopKindAttr", "">:$input_flop_kind, + OptionalParameter<"FlopKindAttr", "">:$output_flop_kind + ); + let mnemonic = "boundary_channel"; + let assemblyFormat = "`<` struct(params) `>`"; +} + +def Xls_BoundaryChannelArrayAttr : + TypedArrayAttrBase; + class GetShapeSplat : StrFunc<"getShapeSplat($" # name # ".getType())">; @@ -1874,9 +1892,8 @@ def Xls_SprocOp : Xls_Op<"sproc", [ region contains Schan declarations and "spawns" of other Sprocs. The only way to create an Sproc is to spawn another Sproc. - Sprocs may be marked as "top" if they EITHER: - a) Have no channels. - b) Have the optional `boundary_channel_names` attribute set. + Sprocs may be marked as "top" if they populate the `boundary_channels` + list per argument. In the latter case, boundary channels (used by XLS for tests etc) are created with the given names. @@ -1926,7 +1943,7 @@ def Xls_SprocOp : Xls_Op<"sproc", [ let arguments = (ins SymbolNameAttr:$sym_name, BoolAttr:$is_top, - OptionalAttr:$boundary_channel_names, + OptionalAttr:$boundary_channels, DefaultValuedAttr:$min_pipeline_stages ); let regions = (region diff --git a/xls/contrib/mlir/testdata/arith_to_xls.mlir b/xls/contrib/mlir/testdata/arith_to_xls.mlir index 5ca06ac10e..40d90314a0 100644 --- a/xls/contrib/mlir/testdata/arith_to_xls.mlir +++ b/xls/contrib/mlir/testdata/arith_to_xls.mlir @@ -357,7 +357,7 @@ func.func @negate(%arg0: bf16) -> bf16 attributes { "xls" = true } { // CHECK: next ( // CHECK-NEXT: %[[X:.*]] = xls.add // CHECK-NEXT: xls.yield %[[X]] : i32 -xls.sproc @sproc(%arg0: !xls.schan) top { +xls.sproc @sproc(%arg0: !xls.schan) top attributes { boundary_channels = [#xls.boundary_channel] } { spawns { xls.yield } diff --git a/xls/contrib/mlir/testdata/extract_as_top_level_module.mlir b/xls/contrib/mlir/testdata/extract_as_top_level_module.mlir index c0db2e3d85..b8b5b5f40a 100644 --- a/xls/contrib/mlir/testdata/extract_as_top_level_module.mlir +++ b/xls/contrib/mlir/testdata/extract_as_top_level_module.mlir @@ -80,7 +80,7 @@ module attributes {test.name = "sproc"} { // CHECK-NOT: func @nope // CHECK: xls.sproc @leaf // CHECK-NOT: xls.sproc @unrelated - // CHECK: xls.sproc @sproc(%arg0: !xls.schan) top attributes {boundary_channel_names = ["arg0"]} + // CHECK: xls.sproc @sproc(%arg0: !xls.schan) top attributes {boundary_channels = [#xls.boundary_channel]} func.func @nope(%arg0: !xls.token, %arg1: i32, %arg2: i1) ->i32 { %0 = arith.constant 1 : i32 return %0 : i32 diff --git a/xls/contrib/mlir/testdata/optimize_spawns.mlir b/xls/contrib/mlir/testdata/optimize_spawns.mlir index 49830a717b..04bb3d2880 100644 --- a/xls/contrib/mlir/testdata/optimize_spawns.mlir +++ b/xls/contrib/mlir/testdata/optimize_spawns.mlir @@ -19,7 +19,7 @@ xls.sproc @fn2(%arg0: !xls.schan, out>) { } // Consumes an argument and passes to a spawn. -// CHECK: xls.sproc @consume_arg(%arg0: !xls.schan, in>) top { +// CHECK: xls.sproc @consume_arg(%arg0: !xls.schan, in>) top // CHECK: spawns { // CHECK: %out, %in = xls.schan>("x") // CHECK: xls.spawn @fn(%arg0) : !xls.schan, in> @@ -29,7 +29,7 @@ xls.sproc @fn2(%arg0: !xls.schan, out>) { // CHECK: xls.yield // CHECK: } // CHECK: } -xls.sproc @consume_arg(%arg0: !xls.schan, in>) top { +xls.sproc @consume_arg(%arg0: !xls.schan, in>) top attributes { boundary_channels = [#xls.boundary_channel] } { spawns { %out, %in = xls.schan>("x") xls.spawn @fn(%in) : !xls.schan, in> @@ -45,7 +45,7 @@ xls.sproc @consume_arg(%arg0: !xls.schan, in>) top { } // Produces a result from a spawn. -// CHECK: xls.sproc @produce_result(%arg0: !xls.schan, out>) top { +// CHECK: xls.sproc @produce_result(%arg0: !xls.schan, out>) top // CHECK: spawns { // CHECK: %out, %in = xls.schan>("x") // CHECK: xls.spawn @fn2(%arg0) : !xls.schan, out> @@ -55,7 +55,7 @@ xls.sproc @consume_arg(%arg0: !xls.schan, in>) top { // CHECK: xls.yield // CHECK: } // CHECK: } -xls.sproc @produce_result(%arg0: !xls.schan, out>) top { +xls.sproc @produce_result(%arg0: !xls.schan, out>) top attributes { boundary_channels = [#xls.boundary_channel] } { spawns { %out, %in = xls.schan>("x") xls.spawn @fn2(%out) : !xls.schan, out> @@ -194,13 +194,13 @@ xls.sproc @unused_args(%arg0: !xls.schan, in>) { // Consumes an argument and passes to a spawn. The interior channel has a // FifoConfig so can't be eliminated. -// CHECK: xls.sproc @consume_arg_fifo(%arg0: !xls.schan, in>) top { +// CHECK: xls.sproc @consume_arg_fifo(%arg0: !xls.schan, in>) top // CHECK: spawns { // CHECK: %out, %in = xls.schan>("x") attributes {fifo_config // CHECK: xls.spawn @fn(%in) : !xls.schan, in> // CHECK: xls.yield %arg0, %out // CHECK: } -xls.sproc @consume_arg_fifo(%arg0: !xls.schan, in>) top { +xls.sproc @consume_arg_fifo(%arg0: !xls.schan, in>) top attributes { boundary_channels = [#xls.boundary_channel] } { spawns { %out, %in = xls.schan>("x") attributes {fifo_config = #fifo} xls.spawn @fn(%in) : !xls.schan, in> @@ -217,13 +217,13 @@ xls.sproc @consume_arg_fifo(%arg0: !xls.schan, in>) top { // Produces a result from a spawn. The interior channel has a FifoConfig so // can't be eliminated. -// CHECK: xls.sproc @produce_result_fifo(%arg0: !xls.schan, out>) top { +// CHECK: xls.sproc @produce_result_fifo(%arg0: !xls.schan, out>) top // CHECK: spawns { // CHECK: %out, %in = xls.schan>("x") attributes {fifo_config // CHECK: xls.spawn @fn2(%out) : !xls.schan, out> // CHECK: xls.yield %in, %arg0 // CHECK: } -xls.sproc @produce_result_fifo(%arg0: !xls.schan, out>) top { +xls.sproc @produce_result_fifo(%arg0: !xls.schan, out>) top attributes { boundary_channels = [#xls.boundary_channel] } { spawns { %out, %in = xls.schan>("x") attributes {fifo_config = #fifo} xls.spawn @fn2(%out) : !xls.schan, out> diff --git a/xls/contrib/mlir/testdata/proc_elaboration.mlir b/xls/contrib/mlir/testdata/proc_elaboration.mlir index adec38f2ff..981f656788 100644 --- a/xls/contrib/mlir/testdata/proc_elaboration.mlir +++ b/xls/contrib/mlir/testdata/proc_elaboration.mlir @@ -38,7 +38,7 @@ // CHECK-NEXT: xls.chan @fetch_arg0 : i32 // CHECK-NEXT: xls.chan @fetch_arg1 : i32 // CHECK-NEXT: xls.instantiate_eproc @fetch (@fetch_arg0 as @req, @fetch_arg1 as @resp) -// CHECK-NEXT: xls.chan @boundary1 {send_supported = false} : i32 +// CHECK-NEXT: xls.chan @boundary1 {fifo_config = #xls.fifo_config, input_flop_kind = #xls, send_supported = false} : i32 // CHECK-NEXT: xls.chan @boundary2 {recv_supported = false} : i32 // CHECK-NEXT: xls.instantiate_eproc @rom (@rom_arg0 as @boundary1, @rom_arg1 as @boundary2) @@ -77,7 +77,10 @@ xls.sproc @proxy(%req: !xls.schan, %resp: !xls.schan) attribu } } -xls.sproc @rom(%req: !xls.schan, %resp: !xls.schan) top attributes {boundary_channel_names = ["boundary1", "boundary2"]} { +xls.sproc @rom(%req: !xls.schan, %resp: !xls.schan) top attributes {boundary_channels = [ + #xls.boundary_channel, input_flop_kind = #xls>, + #xls.boundary_channel +]} { spawns { xls.yield %req, %resp : !xls.schan, !xls.schan } @@ -144,7 +147,9 @@ module { xls.yield %0 : index } } - xls.sproc @some_wrapped_machine(%arg0: !xls.schan, in>, %arg1: !xls.schan, out>, %arg2: !xls.schan, out>) top attributes {boundary_channel_names = ["x['y']", "x['y']1", "x['y']2"]} { + xls.sproc @some_wrapped_machine(%arg0: !xls.schan, in>, %arg1: !xls.schan, out>, %arg2: !xls.schan, out>) top attributes { + boundary_channels = [#xls.boundary_channel, #xls.boundary_channel, #xls.boundary_channel] + } { spawns { %out, %in = xls.schan>("x['y']") %out_0, %in_1 = xls.schan>("x['y']") diff --git a/xls/contrib/mlir/testdata/symbol_dce.mlir b/xls/contrib/mlir/testdata/symbol_dce.mlir index 96193a14f5..d97217710e 100644 --- a/xls/contrib/mlir/testdata/symbol_dce.mlir +++ b/xls/contrib/mlir/testdata/symbol_dce.mlir @@ -21,7 +21,7 @@ xls.sproc @sproc(%arg0: !xls.schan) { // CHECK: @top_sproc // CHECK: @top_extern_sproc xls.extern_sproc @top_extern_sproc (arg0: !xls.schan) -xls.sproc @top_sproc(%arg0: !xls.schan) top { +xls.sproc @top_sproc(%arg0: !xls.schan) top attributes { boundary_channels = [#xls.boundary_channel] } { spawns { xls.spawn @top_extern_sproc(%arg0) : !xls.schan xls.yield diff --git a/xls/contrib/mlir/transforms/proc_elaboration.cc b/xls/contrib/mlir/transforms/proc_elaboration.cc index c2d56eb361..355e12e71b 100644 --- a/xls/contrib/mlir/transforms/proc_elaboration.cc +++ b/xls/contrib/mlir/transforms/proc_elaboration.cc @@ -323,18 +323,19 @@ void ProcElaborationPass::runOnOperation() { OpBuilder builder(sproc); SmallVector boundaryChannels; - if (sproc.getBoundaryChannelNames().has_value()) { - for (auto [arg, name] : llvm::zip(sproc.getChannelArguments(), - *sproc.getBoundaryChannelNames())) { + if (sproc.getBoundaryChannels().has_value()) { + for (auto [arg, boundaryChannelAttr] : llvm::zip( + sproc.getChannelArguments(), *sproc.getBoundaryChannels())) { SchanType schan = cast(arg.getType()); - auto nameAttr = cast(name); + auto boundaryChannel = cast(boundaryChannelAttr); + auto nameAttr = boundaryChannel.getName(); // TODO(jpienaar): Remove unnecessary default args once they are // generated automatically. - auto echan = ChanOp::create(builder, sproc.getLoc(), nameAttr.str(), - schan.getElementType(), - /*fifo_config=*/nullptr, - /*input_flop_kind=*/nullptr, - /*output_flop_kind=*/nullptr); + auto echan = ChanOp::create( + builder, sproc.getLoc(), boundaryChannel.getName(), + schan.getElementType(), boundaryChannel.getFifoConfig(), + boundaryChannel.getInputFlopKind(), + boundaryChannel.getOutputFlopKind()); // We insert the channel in the symbol table, since there might be // a clash with an *eproc* name later, so we need to know. Alternatively // we could have done something similar in the constructor of the @@ -351,6 +352,9 @@ void ProcElaborationPass::runOnOperation() { } boundaryChannels.push_back(echan); } + } else if (!sproc.getChannelArguments().empty()) { + sproc.emitError() << "top sproc has channel arguments but no boundary " + "channels. This is not supported."; } ElaborationInterpreter interpreter; diff --git a/xls/contrib/mlir/util/extraction_utils.cc b/xls/contrib/mlir/util/extraction_utils.cc index 336b9d2f4c..cf6de1d3c3 100644 --- a/xls/contrib/mlir/util/extraction_utils.cc +++ b/xls/contrib/mlir/util/extraction_utils.cc @@ -137,16 +137,19 @@ void addBoundaryChannelNames( }; } - if (op.getBoundaryChannelNames().has_value()) { + if (op.getBoundaryChannels().has_value()) { return; } ::mlir::OpBuilder builder(op.getOperation()); - ::mlir::SmallVector<::mlir::Attribute> boundaryChannelNames; + ::mlir::SmallVector<::mlir::Attribute> boundaryChannels; for (Value value : op.getChannelArguments()) { - boundaryChannelNames.push_back( - builder.getStringAttr(boundaryChannelName(cast(value)))); + BoundaryChannelAttr boundaryChannel = BoundaryChannelAttr::get( + builder.getContext(), + builder.getStringAttr(boundaryChannelName(cast(value))), + FifoConfig(), FlopKindAttr(), FlopKindAttr()); + boundaryChannels.push_back(boundaryChannel); } - op.setBoundaryChannelNamesAttr(builder.getArrayAttr(boundaryChannelNames)); + op.setBoundaryChannelsAttr(builder.getArrayAttr(boundaryChannels)); } } // namespace