diff --git a/include/circt/Dialect/Arc/ArcOps.td b/include/circt/Dialect/Arc/ArcOps.td index 6c55c2069e99..44d5a74bc7f5 100644 --- a/include/circt/Dialect/Arc/ArcOps.td +++ b/include/circt/Dialect/Arc/ArcOps.td @@ -113,7 +113,7 @@ def DefineOp : ArcOp<"define", [ def OutputOp : ArcOp<"output", [ Terminator, - ParentOneOf<["DefineOp", "LutOp", "ClockDomainOp"]>, + ParentOneOf<["DefineOp", "LutOp", "ClockDomainOp", "ExecuteOp"]>, Pure, ReturnLike ]> { @@ -599,6 +599,35 @@ def StateWriteOp : ArcOp<"state_write", [ }]; } +//===----------------------------------------------------------------------===// +// Procedural Ops +//===----------------------------------------------------------------------===// + +def ExecuteOp : ArcOp<"execute", [ + IsolatedFromAbove, + RecursiveMemoryEffects, +]> { + let summary = "Execute an SSACFG region"; + let description = [{ + The `arc.execute` op allows an SSACFG region to be embedded in a parent + graph region, or another SSACFG region. Whenever execution reaches this op, + its body region is executed and the results yielded from the body are + produced as the `arc.execute` op's results. The op is isolated from above. + Any SSA values defined outside the op that are used inside the body have to + be captured as operands and then referred to as entry block arguments in the + body. + }]; + let arguments = (ins Variadic:$inputs); + let results = (outs Variadic:$results); + let regions = (region MinSizedRegion<1>:$body); + let assemblyFormat = [{ + (` ` `(` $inputs^ `:` type($inputs) `)`)? + (`->` `(` type($results)^ `)`)? + attr-dict-with-keyword $body + }]; + let hasRegionVerifier = 1; +} + //===----------------------------------------------------------------------===// // Simulation Orchestration //===----------------------------------------------------------------------===// diff --git a/lib/Dialect/Arc/ArcOps.cpp b/lib/Dialect/Arc/ArcOps.cpp index 68d390999e33..26f71b294ad3 100644 --- a/lib/Dialect/Arc/ArcOps.cpp +++ b/lib/Dialect/Arc/ArcOps.cpp @@ -630,6 +630,15 @@ LogicalResult SimStepOp::verifySymbolUses(SymbolTableCollection &symbolTable) { return success(); } +//===----------------------------------------------------------------------===// +// ExecuteOp +//===----------------------------------------------------------------------===// + +LogicalResult ExecuteOp::verifyRegions() { + return verifyTypeListEquivalence(*this, getInputs().getTypes(), + getBody().getArgumentTypes(), "input"); +} + #include "circt/Dialect/Arc/ArcInterfaces.cpp.inc" #define GET_OP_CLASSES diff --git a/lib/Dialect/Arc/Transforms/LowerArcsToFuncs.cpp b/lib/Dialect/Arc/Transforms/LowerArcsToFuncs.cpp index c3584683916a..b89b79407665 100644 --- a/lib/Dialect/Arc/Transforms/LowerArcsToFuncs.cpp +++ b/lib/Dialect/Arc/Transforms/LowerArcsToFuncs.cpp @@ -58,6 +58,8 @@ struct OutputOpLowering : public OpConversionPattern { LogicalResult matchAndRewrite(arc::OutputOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const final { + if (!isa(op->getParentOp())) + return failure(); rewriter.replaceOpWithNewOp(op, adaptor.getOutputs()); return success(); } @@ -101,7 +103,9 @@ static void populateLegality(ConversionTarget &target) { target.addIllegalOp(); target.addIllegalOp(); - target.addIllegalOp(); + target.addDynamicallyLegalOp([](auto op) { + return !isa(op->getParentOp()); + }); target.addIllegalOp(); } diff --git a/test/Dialect/Arc/basic-errors.mlir b/test/Dialect/Arc/basic-errors.mlir index 350202e089fb..2375c3399275 100644 --- a/test/Dialect/Arc/basic-errors.mlir +++ b/test/Dialect/Arc/basic-errors.mlir @@ -590,3 +590,37 @@ hw.module @InvalidInitType(in %clock: !seq.clock, in %input: i7) { // expected-error @below {{failed to verify that types of initial arguments match result types}} %res = arc.state @Bar(%input) clock %clock initial (%cst: i8) latency 1 : (i7) -> i7 } + +// ----- + +// expected-error @below {{region with at least 1 blocks}} +arc.execute { +} + +// ----- + +%0 = hw.constant 0 : i42 +// expected-error @below {{input type mismatch: input #0}} +// expected-note @below {{expected type: 'i42'}} +// expected-note @below {{actual type: 'i19'}} +arc.execute (%0 : i42) { +^bb0(%arg0: i19): + arc.output +} + +// ----- + +arc.execute -> (i42) { + // expected-error @below {{incorrect number of outputs: expected 1, but got 0}} + arc.output +} + +// ----- + +arc.execute -> (i42) { + %0 = hw.constant 0 : i19 + // expected-error @below {{output type mismatch: output #0}} + // expected-note @below {{expected type: 'i42'}} + // expected-note @below {{actual type: 'i19'}} + arc.output %0 : i19 +} diff --git a/test/Dialect/Arc/basic.mlir b/test/Dialect/Arc/basic.mlir index 4a2a307c6c01..e02afc3b383b 100644 --- a/test/Dialect/Arc/basic.mlir +++ b/test/Dialect/Arc/basic.mlir @@ -1,4 +1,4 @@ -// RUN: circt-opt %s --verify-diagnostics | circt-opt | FileCheck %s +// RUN: circt-opt --verify-diagnostics --verify-roundtrip %s | circt-opt | FileCheck %s // CHECK-LABEL: arc.define @Foo arc.define @Foo(%arg0: i42, %arg1: i9) -> (i42, i9) { @@ -370,3 +370,26 @@ func.func @ReadsWrites(%arg0: !arc.state, %arg1: i42, %arg2: i1) { arc.state_write %arg0 = %arg1 if %arg2 : return } + +func.func @Execute(%arg0: i42) { + // CHECK: arc.execute { + arc.execute { + arc.output + } + // CHECK: arc.execute (%arg0 : i42) { + arc.execute (%arg0 : i42) { + ^bb0(%0: i42): + arc.output + } + // CHECK: arc.execute -> (i42) { + arc.execute -> (i42) { + %0 = hw.constant 1337 : i42 + arc.output %0 : i42 + } + // CHECK: arc.execute (%arg0 : i42) -> (i42) { + arc.execute (%arg0 : i42) -> (i42) { + ^bb0(%0: i42): + arc.output %0 : i42 + } + return +} diff --git a/test/Dialect/Arc/lower-arcs-to-funcs.mlir b/test/Dialect/Arc/lower-arcs-to-funcs.mlir index dd22227821e7..8af9c1fc62b6 100644 --- a/test/Dialect/Arc/lower-arcs-to-funcs.mlir +++ b/test/Dialect/Arc/lower-arcs-to-funcs.mlir @@ -28,3 +28,14 @@ arc.define @sub1(%arg0: i32) -> i32 { arc.output %arg0 : i32 // CHECK-NEXT: return %arg0 : i32 } + +// CHECK-LABEL: hw.module @DontConvertExecuteOps +hw.module @DontConvertExecuteOps(in %arg0: i32, out out0: i32) { + // CHECK: arc.execute + // CHECK: arc.output + %0 = arc.execute (%arg0 : i32) -> (i32) { + ^bb0(%1: i32): + arc.output %1 : i32 + } + hw.output %0 : i32 +}