Skip to content

Commit 292918f

Browse files
[ArcToLLVM] Add arc.execute conversion (llvm#8951)
Add a conversion from `arc.execute` to the LLVM dialect. The conversion mimics `scf.execute_region`, but tries to convert types and block signatures along the way. (No idea why SCF does not do this.) This allows `arc.execute` ops to be lowered to LLVM by inlining the control flow graph into the parent SSACFG region, most likely an `llvm.func`. This now allows Arcilator to simulate designs that contain `llhd.combinational` ops.
1 parent 594787f commit 292918f

File tree

3 files changed

+87
-2
lines changed

3 files changed

+87
-2
lines changed

lib/Conversion/ArcToLLVM/LowerArcToLLVM.cpp

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "circt/Dialect/Arc/ModelInfo.h"
1515
#include "circt/Dialect/Comb/CombOps.h"
1616
#include "circt/Dialect/Seq/SeqOps.h"
17+
#include "circt/Support/ConversionPatternSet.h"
1718
#include "circt/Support/Namespace.h"
1819
#include "mlir/Conversion/ArithToLLVM/ArithToLLVM.h"
1920
#include "mlir/Conversion/ControlFlowToLLVM/ControlFlowToLLVM.h"
@@ -22,7 +23,7 @@
2223
#include "mlir/Conversion/LLVMCommon/ConversionTarget.h"
2324
#include "mlir/Conversion/LLVMCommon/TypeConverter.h"
2425
#include "mlir/Conversion/SCFToControlFlow/SCFToControlFlow.h"
25-
#include "mlir/Dialect/ControlFlow/IR/ControlFlow.h"
26+
#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
2627
#include "mlir/Dialect/Func/IR/FuncOps.h"
2728
#include "mlir/Dialect/Index/IR/IndexOps.h"
2829
#include "mlir/Dialect/LLVMIR/FunctionCallUtils.h"
@@ -598,6 +599,53 @@ struct SimEmitValueOpLowering
598599

599600
} // namespace
600601

602+
static LogicalResult convert(arc::ExecuteOp op, arc::ExecuteOp::Adaptor adaptor,
603+
ConversionPatternRewriter &rewriter,
604+
const TypeConverter &converter) {
605+
// Convert the argument types in the body blocks.
606+
if (failed(rewriter.convertRegionTypes(&op.getBody(), converter)))
607+
return failure();
608+
609+
// Split the block at the current insertion point such that we can branch into
610+
// the `arc.execute` body region, and have `arc.output` branch back to the
611+
// point after the `arc.execute`.
612+
auto *blockBefore = rewriter.getInsertionBlock();
613+
auto *blockAfter =
614+
rewriter.splitBlock(blockBefore, rewriter.getInsertionPoint());
615+
616+
// Branch to the entry block.
617+
rewriter.setInsertionPointToEnd(blockBefore);
618+
mlir::cf::BranchOp::create(rewriter, op.getLoc(), &op.getBody().front(),
619+
adaptor.getInputs());
620+
621+
// Make all `arc.output` terminators branch to the block after the
622+
// `arc.execute` op.
623+
for (auto &block : op.getBody()) {
624+
auto outputOp = dyn_cast<arc::OutputOp>(block.getTerminator());
625+
if (!outputOp)
626+
continue;
627+
rewriter.setInsertionPointToEnd(&block);
628+
rewriter.replaceOpWithNewOp<mlir::cf::BranchOp>(outputOp, blockAfter,
629+
outputOp.getOperands());
630+
}
631+
632+
// Inline the body region between the before and after blocks.
633+
rewriter.inlineRegionBefore(op.getBody(), blockAfter);
634+
635+
// Add arguments to the block after the `arc.execute`, replace the op's
636+
// results with the arguments, then perform block signature conversion.
637+
SmallVector<Value> args;
638+
args.reserve(op.getNumResults());
639+
for (auto result : op.getResults())
640+
args.push_back(blockAfter->addArgument(result.getType(), result.getLoc()));
641+
rewriter.replaceOp(op, args);
642+
auto conversion = converter.convertBlockSignature(blockAfter);
643+
if (!conversion)
644+
return failure();
645+
rewriter.applySignatureConversion(blockAfter, *conversion, &converter);
646+
return success();
647+
}
648+
601649
//===----------------------------------------------------------------------===//
602650
// Pass Implementation
603651
//===----------------------------------------------------------------------===//
@@ -667,7 +715,7 @@ void LowerArcToLLVMPass::runOnOperation() {
667715
});
668716

669717
// Setup the conversion patterns.
670-
RewritePatternSet patterns(&getContext());
718+
ConversionPatternSet patterns(&getContext(), converter);
671719

672720
// MLIR patterns.
673721
populateSCFToControlFlowConversionPatterns(patterns);
@@ -708,6 +756,7 @@ void LowerArcToLLVMPass::runOnOperation() {
708756
ZeroCountOpLowering
709757
>(converter, &getContext());
710758
// clang-format on
759+
patterns.add<ExecuteOp>(convert);
711760

712761
SmallVector<ModelInfo> models;
713762
if (failed(collectModels(getOperation(), models))) {

test/Conversion/ArcToLLVM/lower-arc-to-llvm.mlir

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,3 +243,38 @@ func.func @DontCrashOnI0(%arg0: i1, %arg1: !hw.array<1xi42>) -> i42 {
243243
%1 = hw.array_get %arg1[%0] : !hw.array<1xi42>, i0
244244
return %1 : i42
245245
}
246+
247+
// CHECK-LABEL: llvm.func @ExecuteEmpty
248+
func.func @ExecuteEmpty() {
249+
// CHECK-NEXT: llvm.br [[BB:\^.+]]
250+
// CHECK-NEXT: [[BB]]:
251+
arc.execute {
252+
// CHECK-NEXT: llvm.br [[BB:\^.+]]
253+
arc.output
254+
}
255+
// CHECK-NEXT: [[BB]]:
256+
// CHECK-NEXT: llvm.return
257+
return
258+
}
259+
260+
// CHECK-LABEL: llvm.func @ExecuteWithOperandsAndResults
261+
func.func @ExecuteWithOperandsAndResults(%arg0: i42, %arg1: !hw.array<4xi19>, %arg2: !arc.storage) {
262+
// CHECK-NEXT: llvm.br [[BB:\^.+]](%arg0, %arg1, %arg2 : i42, !llvm.array<4 x i19>, !llvm.ptr)
263+
// CHECK-NEXT: [[BB]]([[ARG0:%.+]]: i42, [[ARG1:%.+]]: !llvm.array<4 x i19>, [[ARG2:%.+]]: !llvm.ptr):
264+
%4:3 = arc.execute (%arg0, %arg1, %arg2 : i42, !hw.array<4xi19>, !arc.storage) -> (i42, !hw.array<4xi19>, !arc.storage) {
265+
^bb0(%0: i42, %1: !hw.array<4xi19>, %2: !arc.storage):
266+
// CHECK-NEXT: llvm.br [[BB:\^.+]]([[ARG2]] : !llvm.ptr)
267+
cf.br ^bb1(%2 : !arc.storage)
268+
^bb1(%3: !arc.storage):
269+
// CHECK-NEXT: [[BB]]([[ARG2:%.+]]: !llvm.ptr):
270+
// CHECK-NEXT: llvm.br [[BB:\^.+]]([[ARG0]], [[ARG1]], [[ARG2]] : i42, !llvm.array<4 x i19>, !llvm.ptr)
271+
arc.output %0, %1, %3 : i42, !hw.array<4xi19>, !arc.storage
272+
}
273+
// CHECK-NEXT: [[BB]]([[ARG0:%.+]]: i42, [[ARG1:%.+]]: !llvm.array<4 x i19>, [[ARG2:%.+]]: !llvm.ptr):
274+
// CHECK-NEXT: llvm.call @Dummy([[ARG0:%.+]], [[ARG1:%.+]], [[ARG2:%.+]]) : (i42, !llvm.array<4 x i19>, !llvm.ptr) -> ()
275+
call @Dummy(%4#0, %4#1, %4#2) : (i42, !hw.array<4xi19>, !arc.storage) -> ()
276+
// CHECK-NEXT: llvm.return
277+
return
278+
}
279+
280+
func.func private @Dummy(%arg0: i42, %arg1: !hw.array<4xi19>, %arg2: !arc.storage)

tools/arcilator/arcilator.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,7 @@ int main(int argc, char **argv) {
641641
// Dialect passes:
642642
arc::registerPasses();
643643
registerConvertToArcsPass();
644+
registerLowerArcToLLVMPass();
644645
}
645646

646647
// Register any pass manager command line options.

0 commit comments

Comments
 (0)