mirror of https://github.com/llvm/circt.git
[Ibis] Add ibistool (#6206)
Adds `ibistool` - a tool for driving Ibis lowerings. The tool has two modes - low-level and high-level Ibis lowering. Alongside this, introduce a set of Ibis pass pipelines which other users may load to ensure that they're lowering ibis constructs in the standard order.
This commit is contained in:
parent
c8ab1e1163
commit
6b73873825
|
@ -0,0 +1,29 @@
|
|||
//===- IbisPassPipelines.h - Ibis pass pipelines -----------------*- 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_IBIS_IBISPASSPIPELINES_H
|
||||
#define CIRCT_DIALECT_IBIS_IBISPASSPIPELINES_H
|
||||
|
||||
#include "circt/Dialect/Ibis/IbisPasses.h"
|
||||
#include "mlir/Pass/PassManager.h"
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
namespace circt {
|
||||
namespace ibis {
|
||||
|
||||
// Loads a pass pipeline to transform low-level Ibis constructs.
|
||||
void loadIbisLowLevelPassPipeline(mlir::PassManager &pm);
|
||||
|
||||
// Loads a pass pipeline to transform high-level Ibis constructs.
|
||||
void loadIbisHighLevelPassPipeline(mlir::PassManager &pm);
|
||||
|
||||
} // namespace ibis
|
||||
} // namespace circt
|
||||
|
||||
#endif // CIRCT_DIALECT_IBIS_IBISPASSPIPELINES_H
|
|
@ -21,16 +21,17 @@ namespace ibis {
|
|||
#define GEN_PASS_DECL_IBISTUNNELING
|
||||
#include "circt/Dialect/Ibis/IbisPasses.h.inc"
|
||||
|
||||
std::unique_ptr<Pass> createCallPrepPass();
|
||||
std::unique_ptr<Pass> createContainerizePass();
|
||||
std::unique_ptr<Pass> createTunnelingPass(const IbisTunnelingOptions & = {});
|
||||
std::unique_ptr<Pass> createPortrefLoweringPass();
|
||||
std::unique_ptr<Pass> createCleanSelfdriversPass();
|
||||
std::unique_ptr<Pass> createContainersToHWPass();
|
||||
std::unique_ptr<Pass> createArgifyBlocksPass();
|
||||
std::unique_ptr<Pass> createReblockPass();
|
||||
std::unique_ptr<Pass> createInlineSBlocksPass();
|
||||
std::unique_ptr<Pass> createConvertCFToHandshakePass();
|
||||
std::unique_ptr<mlir::Pass> createCallPrepPass();
|
||||
std::unique_ptr<mlir::Pass> createContainerizePass();
|
||||
std::unique_ptr<mlir::Pass>
|
||||
createTunnelingPass(const IbisTunnelingOptions & = {});
|
||||
std::unique_ptr<mlir::Pass> createPortrefLoweringPass();
|
||||
std::unique_ptr<mlir::Pass> createCleanSelfdriversPass();
|
||||
std::unique_ptr<mlir::Pass> createContainersToHWPass();
|
||||
std::unique_ptr<mlir::Pass> createArgifyBlocksPass();
|
||||
std::unique_ptr<mlir::Pass> createReblockPass();
|
||||
std::unique_ptr<mlir::Pass> createInlineSBlocksPass();
|
||||
std::unique_ptr<mlir::Pass> createConvertCFToHandshakePass();
|
||||
|
||||
/// Generate the code for registering passes.
|
||||
#define GEN_PASS_REGISTRATION
|
||||
|
|
|
@ -10,6 +10,7 @@ set(CIRCT_INTEGRATION_TEST_DEPENDS
|
|||
esi-collateral
|
||||
firtool
|
||||
hlstool
|
||||
ibistool
|
||||
handshake-runner
|
||||
)
|
||||
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
// RUN: circt-opt --ibis-containerize --ibis-tunneling --ibis-lower-portrefs \
|
||||
// RUN: --canonicalize --ibis-clean-selfdrivers --canonicalize \
|
||||
// RUN: --ibis-convert-containers-to-hw --pipeline-explicit-regs \
|
||||
// RUN: --lower-pipeline-to-hw --lower-seq-to-sv \
|
||||
// RUN: --export-verilog -o %t_lo.mlir %s > %t.sv
|
||||
// RUN: ibistool -lo %s
|
||||
|
||||
// A class hierarchy with a shared parent, and accessing between the children
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ add_circt_dialect_library(CIRCTIbisTransforms
|
|||
IbisReblockPass.cpp
|
||||
IbisInlineSBlocksPass.cpp
|
||||
IbisConvertCFToHandshake.cpp
|
||||
IbisPassPipelines.cpp
|
||||
|
||||
DEPENDS
|
||||
CIRCTIbisTransformsIncGen
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
//===- IbisPassPipelines.cpp - Ibis pass pipelines ------------------------===//
|
||||
//
|
||||
// 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/Ibis/IbisPassPipelines.h"
|
||||
#include "circt/Dialect/Ibis/IbisOps.h"
|
||||
#include "circt/Transforms/Passes.h"
|
||||
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
|
||||
#include "mlir/Transforms/Passes.h"
|
||||
|
||||
using namespace mlir;
|
||||
using namespace circt;
|
||||
using namespace ibis;
|
||||
|
||||
/// Create a simple canonicalizer pass.
|
||||
static std::unique_ptr<Pass> createSimpleCanonicalizerPass() {
|
||||
mlir::GreedyRewriteConfig config;
|
||||
config.useTopDownTraversal = true;
|
||||
config.enableRegionSimplification = false;
|
||||
return mlir::createCanonicalizerPass(config);
|
||||
}
|
||||
|
||||
void circt::ibis::loadIbisLowLevelPassPipeline(mlir::PassManager &pm) {
|
||||
pm.addPass(createContainerizePass());
|
||||
pm.addPass(createTunnelingPass(IbisTunnelingOptions{"", ""}));
|
||||
pm.addPass(createPortrefLoweringPass());
|
||||
pm.addPass(createSimpleCanonicalizerPass());
|
||||
pm.addPass(createCleanSelfdriversPass());
|
||||
pm.addPass(createContainersToHWPass());
|
||||
}
|
||||
|
||||
void circt::ibis::loadIbisHighLevelPassPipeline(mlir::PassManager &pm) {
|
||||
pm.nest<ibis::MethodOp>().addPass(ibis::createInlineSBlocksPass());
|
||||
pm.addPass(mlir::createMem2Reg());
|
||||
|
||||
// TODO @mortbopet: Add a verification pass to ensure that there are no more
|
||||
// memref.alloca's - we want all memories to be mem2reg'able, unless they are
|
||||
// member variable accesses.
|
||||
// - just add it as an illegal op.
|
||||
|
||||
// Now, perform SSA maximizations.
|
||||
pm.addPass(circt::createMaximizeSSAPass());
|
||||
|
||||
// SSA maximal form achieved. Reconstruct the Ibis sblocks.
|
||||
pm.nest<ibis::MethodOp>().addPass(ibis::createReblockPass());
|
||||
pm.addPass(ibis::createArgifyBlocksPass());
|
||||
pm.addPass(createSimpleCanonicalizerPass());
|
||||
|
||||
// Make the CFG a binary tree by inserting merge blocks.
|
||||
pm.addPass(circt::createInsertMergeBlocksPass());
|
||||
|
||||
// Perform dataflow conversion
|
||||
pm.nest<ibis::ClassOp>().addPass(ibis::createConvertCFToHandshakePass());
|
||||
// Canonicalize - necessary after handshake conversion to clean up a lot of
|
||||
// stuff e.g. simple branches.
|
||||
pm.addPass(createSimpleCanonicalizerPass());
|
||||
}
|
|
@ -29,6 +29,7 @@ set(CIRCT_TEST_DEPENDS
|
|||
handshake-runner
|
||||
firtool
|
||||
hlstool
|
||||
ibistool
|
||||
om-linker
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
// RUN: ibistool --hi --post-ibis-ir %s | FileCheck %s
|
||||
|
||||
// CHECK-LABEL: ibis.class @ToHandshake {
|
||||
// CHECK: %[[VAL_0:.*]] = ibis.this @ToHandshake
|
||||
// CHECK: ibis.method.df @foo(%[[VAL_1:.*]]: index, %[[VAL_2:.*]]: index, %[[VAL_3:.*]]: i1, %[[VAL_4:.*]]: none) -> (i32, none) {
|
||||
// CHECK: %[[VAL_5:.*]] = handshake.constant %[[VAL_4]] {value = 2 : i32} : i32
|
||||
// CHECK: %[[VAL_6:.*]] = handshake.constant %[[VAL_4]] {value = 1 : index} : index
|
||||
// CHECK: %[[VAL_7:.*]] = handshake.constant %[[VAL_4]] {value = 0 : i32} : i32
|
||||
// CHECK: %[[VAL_8:.*]] = handshake.buffer [1] seq %[[VAL_9:.*]] {initValues = [0]} : i1
|
||||
// CHECK: %[[VAL_10:.*]] = handshake.mux %[[VAL_8]] {{\[}}%[[VAL_4]], %[[VAL_11:.*]]] : i1, none
|
||||
// CHECK: %[[VAL_12:.*]] = handshake.mux %[[VAL_8]] {{\[}}%[[VAL_1]], %[[VAL_13:.*]]] : i1, index
|
||||
// CHECK: %[[VAL_14:.*]] = handshake.mux %[[VAL_8]] {{\[}}%[[VAL_7]], %[[VAL_15:.*]]] : i1, i32
|
||||
// CHECK: %[[VAL_16:.*]] = handshake.mux %[[VAL_8]] {{\[}}%[[VAL_2]], %[[VAL_17:.*]]] : i1, index
|
||||
// CHECK: %[[VAL_18:.*]] = handshake.mux %[[VAL_8]] {{\[}}%[[VAL_5]], %[[VAL_19:.*]]] : i1, i32
|
||||
// CHECK: %[[VAL_20:.*]] = handshake.mux %[[VAL_8]] {{\[}}%[[VAL_6]], %[[VAL_21:.*]]] : i1, index
|
||||
// CHECK: %[[VAL_22:.*]] = handshake.mux %[[VAL_8]] {{\[}}%[[VAL_7]], %[[VAL_23:.*]]] : i1, i32
|
||||
// CHECK: %[[VAL_9]] = arith.cmpi slt, %[[VAL_12]], %[[VAL_16]] : index
|
||||
// CHECK: %[[VAL_24:.*]], %[[VAL_25:.*]] = handshake.cond_br %[[VAL_9]], %[[VAL_12]] : index
|
||||
// CHECK: %[[VAL_26:.*]], %[[VAL_27:.*]] = handshake.cond_br %[[VAL_9]], %[[VAL_14]] : i32
|
||||
// CHECK: %[[VAL_17]], %[[VAL_28:.*]] = handshake.cond_br %[[VAL_9]], %[[VAL_16]] : index
|
||||
// CHECK: %[[VAL_19]], %[[VAL_29:.*]] = handshake.cond_br %[[VAL_9]], %[[VAL_18]] : i32
|
||||
// CHECK: %[[VAL_21]], %[[VAL_30:.*]] = handshake.cond_br %[[VAL_9]], %[[VAL_20]] : index
|
||||
// CHECK: %[[VAL_23]], %[[VAL_31:.*]] = handshake.cond_br %[[VAL_9]], %[[VAL_22]] : i32
|
||||
// CHECK: %[[VAL_32:.*]], %[[VAL_33:.*]] = handshake.cond_br %[[VAL_9]], %[[VAL_10]] : none
|
||||
// CHECK: %[[VAL_34:.*]] = arith.index_cast %[[VAL_24]] : index to i32
|
||||
// CHECK: %[[VAL_35:.*]] = arith.remsi %[[VAL_26]], %[[VAL_19]] : i32
|
||||
// CHECK: %[[VAL_36:.*]] = arith.cmpi eq, %[[VAL_35]], %[[VAL_23]] : i32
|
||||
// CHECK: %[[VAL_37:.*]], %[[VAL_38:.*]] = handshake.cond_br %[[VAL_36]], %[[VAL_26]] : i32
|
||||
// CHECK: %[[VAL_39:.*]], %[[VAL_40:.*]] = handshake.cond_br %[[VAL_36]], %[[VAL_32]] : none
|
||||
// CHECK: %[[VAL_41:.*]], %[[VAL_42:.*]] = handshake.cond_br %[[VAL_36]], %[[VAL_34]] : i32
|
||||
// CHECK: %[[VAL_43:.*]] = arith.addi %[[VAL_37]], %[[VAL_41]] : i32
|
||||
// CHECK: %[[VAL_44:.*]] = arith.subi %[[VAL_38]], %[[VAL_42]] : i32
|
||||
// CHECK: %[[VAL_15]] = handshake.mux %[[VAL_45:.*]] {{\[}}%[[VAL_44]], %[[VAL_43]]] : index, i32
|
||||
// CHECK: %[[VAL_11]], %[[VAL_45]] = handshake.control_merge %[[VAL_40]], %[[VAL_39]] : none, index
|
||||
// CHECK: %[[VAL_13]] = arith.addi %[[VAL_24]], %[[VAL_21]] : index
|
||||
// CHECK: ibis.return %[[VAL_27]], %[[VAL_33]] : i32, none
|
||||
// CHECK: }
|
||||
// CHECK: }
|
||||
|
||||
ibis.class @ToHandshake {
|
||||
%this = ibis.this @ToHandshake
|
||||
ibis.method @foo(%a: index, %b: index, %c : i1) -> i32 {
|
||||
%sum = memref.alloca () : memref<i32>
|
||||
%c0_i32 = arith.constant 0 : i32
|
||||
memref.store %c0_i32, %sum[] : memref<i32>
|
||||
%c1 = arith.constant 1 : index
|
||||
%c2 = arith.constant 2 : i32
|
||||
%c0 = arith.constant 0 : i32
|
||||
scf.for %i = %a to %b step %c1 {
|
||||
%acc = memref.load %sum[] : memref<i32>
|
||||
%i_i32 = arith.index_cast %i : index to i32
|
||||
%rem = arith.remsi %acc, %c2 : i32
|
||||
%cond = arith.cmpi eq, %rem, %c0 : i32
|
||||
%res = scf.if %cond -> (i32) {
|
||||
%v = arith.addi %acc, %i_i32 : i32
|
||||
scf.yield %v : i32
|
||||
} else {
|
||||
%v = arith.subi %acc, %i_i32 : i32
|
||||
scf.yield %v : i32
|
||||
}
|
||||
memref.store %res, %sum[] : memref<i32>
|
||||
}
|
||||
%res = memref.load %sum[] : memref<i32>
|
||||
ibis.return %res : i32
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
// RUN: ibistool %s --lo --post-ibis-ir | FileCheck %s --check-prefix=CHECK-POST-IBIS
|
||||
// RUN: ibistool %s --lo --ir | FileCheck %s --check-prefix=CHECK-IR
|
||||
// RUN: ibistool %s --lo --verilog | FileCheck %s --check-prefix=CHECK-VERILOG
|
||||
|
||||
// CHECK-POST-IBIS-LABEL: hw.module @A_B(
|
||||
// CHECK-POST-IBIS-SAME: %[[VAL_0:.*]]: !seq.clock, %[[VAL_1:.*]]: i1) -> (p_out: i1) {
|
||||
// CHECK-POST-IBIS: %[[VAL_2:.*]] = seq.compreg %[[VAL_1]], %[[VAL_0]] : i1
|
||||
// CHECK-POST-IBIS: hw.output %[[VAL_2]] : i1
|
||||
// CHECK-POST-IBIS: }
|
||||
|
||||
// CHECK-POST-IBIS-LABEL: hw.module @A(
|
||||
// CHECK-POST-IBIS-SAME: %[[VAL_0:.*]]: i1, %[[VAL_1:.*]]: !seq.clock) -> (out: i1) {
|
||||
// CHECK-POST-IBIS: %[[VAL_2:.*]] = hw.instance "A_B" @A_B(p_clk: %[[VAL_1]]: !seq.clock, p_in: %[[VAL_0]]: i1) -> (p_out: i1)
|
||||
// CHECK-POST-IBIS: hw.output %[[VAL_2]] : i1
|
||||
// CHECK-POST-IBIS: }
|
||||
|
||||
// CHECK-IR-LABEL: hw.module @A_B(
|
||||
// CHECK-IR-SAME: %[[VAL_0:.*]]: i1, %[[VAL_1:.*]]: i1) -> (p_out: i1) {
|
||||
// CHECK-IR: %[[VAL_2:.*]] = sv.reg : !hw.inout<i1>
|
||||
// CHECK-IR: %[[VAL_3:.*]] = sv.read_inout %[[VAL_2]] : !hw.inout<i1>
|
||||
// CHECK-IR: sv.alwaysff(posedge %[[VAL_0]]) {
|
||||
// CHECK-IR: sv.passign %[[VAL_2]], %[[VAL_1]] : i1
|
||||
// CHECK-IR: }
|
||||
// CHECK-IR: hw.output %[[VAL_3]] : i1
|
||||
// CHECK-IR: }
|
||||
|
||||
// CHECK-IR-LABEL: hw.module @A(
|
||||
// CHECK-IR-SAME: %[[VAL_0:.*]]: i1, %[[VAL_1:.*]]: i1) -> (out: i1) {
|
||||
// CHECK-IR: %[[VAL_2:.*]] = hw.instance "A_B" @A_B(p_clk: %[[VAL_1]]: i1, p_in: %[[VAL_0]]: i1) -> (p_out: i1)
|
||||
// CHECK-IR: hw.output %[[VAL_2]] : i1
|
||||
// CHECK-IR: }
|
||||
|
||||
// CHECK-VERILOG-LABEL: module A_B(
|
||||
// CHECK-VERILOG: input p_clk,
|
||||
// CHECK-VERILOG: p_in,
|
||||
// CHECK-VERILOG: output p_out
|
||||
// CHECK-VERILOG: );
|
||||
// CHECK-VERILOG: reg r;
|
||||
// CHECK-VERILOG: always_ff @(posedge p_clk)
|
||||
// CHECK-VERILOG: r <= p_in;
|
||||
// CHECK-VERILOG: assign p_out = r;
|
||||
// CHECK-VERILOG: endmodule
|
||||
|
||||
// CHECK-VERILOG-LABEL: module A(
|
||||
// CHECK-VERILOG: input in,
|
||||
// CHECK-VERILOG: clk,
|
||||
// CHECK-VERILOG: output out
|
||||
// CHECK-VERILOG: );
|
||||
// CHECK-VERILOG: A_B A_B (
|
||||
// CHECK-VERILOG: .p_clk (clk),
|
||||
// CHECK-VERILOG: .p_in (in),
|
||||
// CHECK-VERILOG: .p_out (out)
|
||||
// CHECK-VERILOG: );
|
||||
// CHECK-VERILOG: endmodule
|
||||
|
||||
ibis.class @A {
|
||||
%this = ibis.this @A
|
||||
ibis.port.input @in : i1
|
||||
ibis.port.output @out : i1
|
||||
ibis.port.input @clk : !seq.clock
|
||||
|
||||
ibis.container@B {
|
||||
%B_this = ibis.this @B
|
||||
%parent = ibis.path [
|
||||
#ibis.step<parent : !ibis.scoperef<@A>>
|
||||
]
|
||||
%a_in = ibis.get_port %parent, @in : !ibis.scoperef<@A> -> !ibis.portref<out i1>
|
||||
%a_clk = ibis.get_port %parent, @clk : !ibis.scoperef<@A> -> !ibis.portref<out !seq.clock>
|
||||
%a_out = ibis.get_port %parent, @out : !ibis.scoperef<@A> -> !ibis.portref<in i1>
|
||||
%in = ibis.port.read %a_in : !ibis.portref<out i1>
|
||||
%clk = ibis.port.read %a_clk : !ibis.portref<out !seq.clock>
|
||||
%r = seq.compreg %in, %clk: i1
|
||||
ibis.port.write %a_out, %r : !ibis.portref<in i1>
|
||||
}
|
||||
}
|
|
@ -58,7 +58,7 @@ tool_dirs = [
|
|||
tools = [
|
||||
'arcilator', 'circt-as', 'circt-capi-ir-test', 'circt-capi-om-test',
|
||||
'circt-capi-firrtl-test', 'circt-dis', 'circt-opt', 'circt-reduce',
|
||||
'circt-translate', 'firtool', 'hlstool', 'om-linker'
|
||||
'circt-translate', 'firtool', 'hlstool', 'om-linker', 'ibistool'
|
||||
]
|
||||
|
||||
# Enable Verilator if it has been detected.
|
||||
|
|
|
@ -15,3 +15,4 @@ add_subdirectory(llhd-sim)
|
|||
add_subdirectory(om-linker)
|
||||
add_subdirectory(py-split-input-file)
|
||||
add_subdirectory(hlstool)
|
||||
add_subdirectory(ibistool)
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
set(LLVM_LINK_COMPONENTS
|
||||
Support
|
||||
)
|
||||
|
||||
add_circt_tool(ibistool
|
||||
ibistool.cpp
|
||||
)
|
||||
llvm_update_compile_flags(ibistool)
|
||||
target_link_libraries(ibistool
|
||||
PRIVATE
|
||||
|
||||
CIRCTExportChiselInterface
|
||||
CIRCTExportVerilog
|
||||
CIRCTHandshake
|
||||
CIRCTHandshakeToHW
|
||||
CIRCTHandshakeTransforms
|
||||
CIRCTHW
|
||||
CIRCTHWTransforms
|
||||
CIRCTSeq
|
||||
CIRCTSeqToSV
|
||||
CIRCTSeqTransforms
|
||||
CIRCTCFToHandshake
|
||||
CIRCTSV
|
||||
CIRCTSVTransforms
|
||||
CIRCTIbis
|
||||
CIRCTIbisTransforms
|
||||
CIRCTTransforms
|
||||
CIRCTPipelineOps
|
||||
CIRCTPipelineToHW
|
||||
CIRCTDCToHW
|
||||
CIRCTHandshakeToDC
|
||||
|
||||
CIRCTPipelineTransforms
|
||||
CIRCTDC
|
||||
CIRCTDCTransforms
|
||||
CIRCTESI
|
||||
|
||||
MLIRIR
|
||||
MLIRLLVMDialect
|
||||
MLIRMemRefDialect
|
||||
MLIROptLib
|
||||
MLIRParser
|
||||
MLIRControlFlowDialect
|
||||
MLIRSupport
|
||||
MLIRTransforms
|
||||
MLIRSCFToControlFlow
|
||||
)
|
|
@ -0,0 +1,434 @@
|
|||
//===- ibistool.cpp - The ibistool utility for working with the Ibis dialect =//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements 'ibistool', which composes together a variety of
|
||||
// CIRCT libraries that can be used to realise an Ibis-based lowering flow.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "mlir/Conversion/Passes.h"
|
||||
#include "mlir/Dialect/Affine/IR/AffineOps.h"
|
||||
#include "mlir/Dialect/Arith/IR/Arith.h"
|
||||
#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
|
||||
#include "mlir/Dialect/Func/IR/FuncOps.h"
|
||||
#include "mlir/Dialect/MemRef/IR/MemRef.h"
|
||||
#include "mlir/IR/AsmState.h"
|
||||
#include "mlir/IR/BuiltinOps.h"
|
||||
#include "mlir/Parser/Parser.h"
|
||||
#include "mlir/Pass/Pass.h"
|
||||
#include "mlir/Pass/PassInstrumentation.h"
|
||||
#include "mlir/Pass/PassManager.h"
|
||||
#include "mlir/Support/FileUtilities.h"
|
||||
#include "mlir/Support/Timing.h"
|
||||
#include "mlir/Support/ToolUtilities.h"
|
||||
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
|
||||
#include "mlir/Transforms/Passes.h"
|
||||
|
||||
#include "llvm/Support/Chrono.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/InitLLVM.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/SourceMgr.h"
|
||||
#include "llvm/Support/ToolOutputFile.h"
|
||||
|
||||
#include "circt/Conversion/ExportVerilog.h"
|
||||
#include "circt/Conversion/Passes.h"
|
||||
#include "circt/Dialect/DC/DCDialect.h"
|
||||
#include "circt/Dialect/DC/DCPasses.h"
|
||||
#include "circt/Dialect/ESI/ESIDialect.h"
|
||||
#include "circt/Dialect/ESI/ESIPasses.h"
|
||||
#include "circt/Dialect/Ibis/IbisDialect.h"
|
||||
#include "circt/Dialect/Ibis/IbisPassPipelines.h"
|
||||
#include "circt/Dialect/Ibis/IbisPasses.h"
|
||||
#include "circt/Dialect/Pipeline/PipelineDialect.h"
|
||||
#include "circt/Dialect/Pipeline/PipelinePasses.h"
|
||||
#include "circt/Dialect/SV/SVDialect.h"
|
||||
#include "circt/Dialect/SV/SVPasses.h"
|
||||
#include "circt/Dialect/Seq/SeqDialect.h"
|
||||
#include "circt/Dialect/Seq/SeqPasses.h"
|
||||
#include "circt/Support/LoweringOptions.h"
|
||||
#include "circt/Support/LoweringOptionsParser.h"
|
||||
#include "circt/Support/Version.h"
|
||||
#include "circt/Transforms/Passes.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace mlir;
|
||||
using namespace circt;
|
||||
using namespace ibis;
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Tool options
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
static cl::OptionCategory mainCategory("ibistool Options");
|
||||
|
||||
static cl::opt<std::string> inputFilename(cl::Positional,
|
||||
cl::desc("<input file>"),
|
||||
cl::init("-"), cl::cat(mainCategory));
|
||||
|
||||
static cl::opt<std::string> outputFilename(
|
||||
"o", cl::desc("Output filename, or directory for split output"),
|
||||
cl::value_desc("filename"), cl::init("-"), cl::cat(mainCategory));
|
||||
|
||||
static cl::opt<bool>
|
||||
splitInputFile("split-input-file",
|
||||
cl::desc("Split the input file into pieces and process each "
|
||||
"chunk independently"),
|
||||
cl::init(false), cl::Hidden, cl::cat(mainCategory));
|
||||
|
||||
static cl::opt<bool>
|
||||
verifyDiagnostics("verify-diagnostics",
|
||||
cl::desc("Check that emitted diagnostics match "
|
||||
"expected-* lines on the corresponding line"),
|
||||
cl::init(false), cl::Hidden, cl::cat(mainCategory));
|
||||
|
||||
static cl::opt<bool>
|
||||
verbosePassExecutions("verbose-pass-executions",
|
||||
cl::desc("Log executions of toplevel module passes"),
|
||||
cl::init(false), cl::cat(mainCategory));
|
||||
|
||||
static cl::opt<bool>
|
||||
verifyPasses("verify-each",
|
||||
cl::desc("Run the verifier after each transformation pass"),
|
||||
cl::init(true), cl::cat(mainCategory));
|
||||
|
||||
static cl::opt<bool>
|
||||
allowUnregisteredDialects("allow-unregistered-dialects",
|
||||
cl::desc("Allow unknown dialects in the input"),
|
||||
cl::init(false), cl::Hidden,
|
||||
cl::cat(mainCategory));
|
||||
|
||||
enum OutputFormatKind {
|
||||
OutputLoweredIbis,
|
||||
OutputIR,
|
||||
OutputVerilog,
|
||||
OutputSplitVerilog
|
||||
};
|
||||
|
||||
static cl::opt<OutputFormatKind> outputFormat(
|
||||
cl::desc("Specify output format:"),
|
||||
cl::values(
|
||||
clEnumValN(OutputLoweredIbis, "post-ibis-ir",
|
||||
"Emit IR after Ibis constructs have been lowered away"),
|
||||
clEnumValN(OutputIR, "ir", "Emit pre-emission IR"),
|
||||
clEnumValN(OutputVerilog, "verilog", "Emit Verilog"),
|
||||
clEnumValN(OutputSplitVerilog, "split-verilog",
|
||||
"Emit Verilog (one file per module; specify "
|
||||
"directory with -o=<dir>)")),
|
||||
cl::init(OutputVerilog), cl::cat(mainCategory));
|
||||
|
||||
static cl::opt<bool>
|
||||
traceIVerilog("sv-trace-iverilog",
|
||||
cl::desc("Add tracing to an iverilog simulated module"),
|
||||
cl::init(false), cl::cat(mainCategory));
|
||||
|
||||
enum FlowKind { HiIbis, LoIbis };
|
||||
static cl::opt<FlowKind>
|
||||
flowKind(cl::desc("Specify flow kind:"),
|
||||
cl::values(clEnumValN(HiIbis, "hi", "High-level Ibis flow"),
|
||||
clEnumValN(LoIbis, "lo", "Low-level Ibis flow")),
|
||||
cl::Required, cl::cat(mainCategory));
|
||||
|
||||
static LoweringOptionsOption loweringOptions(mainCategory);
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// (Configurable) pass pipelines
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/// Create a simple canonicalizer pass.
|
||||
static std::unique_ptr<Pass> createSimpleCanonicalizerPass() {
|
||||
mlir::GreedyRewriteConfig config;
|
||||
config.useTopDownTraversal = true;
|
||||
config.enableRegionSimplification = false;
|
||||
return mlir::createCanonicalizerPass(config);
|
||||
}
|
||||
|
||||
static void loadHighLevelControlflowTransformsPipeline(OpPassManager &pm) {
|
||||
pm.addPass(mlir::createLowerAffinePass());
|
||||
pm.addPass(mlir::createConvertSCFToCFPass());
|
||||
pm.addPass(createSimpleCanonicalizerPass());
|
||||
}
|
||||
|
||||
static void loadHandshakeTransformsPipeline(OpPassManager &pm) {
|
||||
pm.addPass(circt::createCFToHandshakePass(
|
||||
/*sourceConstants=*/false,
|
||||
/*disableTaskPipelining=*/false));
|
||||
pm.addPass(createSimpleCanonicalizerPass());
|
||||
pm.nest<handshake::FuncOp>().addPass(
|
||||
handshake::createHandshakeMaterializeForksSinksPass());
|
||||
pm.addPass(createSimpleCanonicalizerPass());
|
||||
pm.nest<handshake::FuncOp>().addPass(
|
||||
handshake::createHandshakeInsertBuffersPass("all", 2));
|
||||
pm.addPass(createSimpleCanonicalizerPass());
|
||||
}
|
||||
|
||||
static void loadDCTransformsPipeline(OpPassManager &pm) {
|
||||
pm.addPass(circt::createHandshakeToDCPass());
|
||||
pm.addPass(createSimpleCanonicalizerPass());
|
||||
pm.addPass(circt::createDCToHWPass());
|
||||
}
|
||||
|
||||
static void loadESILoweringPipeline(OpPassManager &pm) {
|
||||
pm.addPass(circt::esi::createESIPortLoweringPass());
|
||||
pm.addPass(circt::esi::createESIPhysicalLoweringPass());
|
||||
pm.addPass(circt::esi::createESItoHWPass());
|
||||
}
|
||||
|
||||
static void loadHWLoweringPipeline(OpPassManager &pm) {
|
||||
pm.addPass(createSimpleCanonicalizerPass());
|
||||
pm.nest<hw::HWModuleOp>().addPass(circt::seq::createLowerSeqHLMemPass());
|
||||
pm.addPass(sv::createHWMemSimImplPass(false, false));
|
||||
pm.addPass(circt::createLowerSeqToSVPass());
|
||||
pm.nest<hw::HWModuleOp>().addPass(sv::createHWCleanupPass());
|
||||
pm.addPass(mlir::createCSEPass());
|
||||
|
||||
// Legalize unsupported operations within the modules.
|
||||
pm.nest<hw::HWModuleOp>().addPass(sv::createHWLegalizeModulesPass());
|
||||
pm.addPass(createSimpleCanonicalizerPass());
|
||||
|
||||
// Tidy up the IR to improve verilog emission quality.
|
||||
auto &modulePM = pm.nest<hw::HWModuleOp>();
|
||||
modulePM.addPass(sv::createPrettifyVerilogPass());
|
||||
}
|
||||
|
||||
static void loadPipelineLoweringPipeline(OpPassManager &pm) {
|
||||
pm.addPass(pipeline::createExplicitRegsPass());
|
||||
pm.addPass(createPipelineToHWPass());
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Tool driver code
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
static void loadLowLevelPassPipeline(
|
||||
PassManager &pm, ModuleOp module,
|
||||
std::optional<std::unique_ptr<llvm::ToolOutputFile>> &outputFile) {
|
||||
loadPipelineLoweringPipeline(pm);
|
||||
loadESILoweringPipeline(pm);
|
||||
loadHWLoweringPipeline(pm);
|
||||
if (traceIVerilog)
|
||||
pm.addPass(circt::sv::createSVTraceIVerilogPass());
|
||||
|
||||
if (loweringOptions.getNumOccurrences())
|
||||
loweringOptions.setAsAttribute(module);
|
||||
if (outputFormat == OutputVerilog) {
|
||||
pm.addPass(createExportVerilogPass((*outputFile)->os()));
|
||||
} else if (outputFormat == OutputSplitVerilog) {
|
||||
pm.addPass(createExportSplitVerilogPass(outputFilename));
|
||||
}
|
||||
}
|
||||
|
||||
static void loadIbisHiFlow(
|
||||
PassManager &pm, ModuleOp module,
|
||||
std::optional<std::unique_ptr<llvm::ToolOutputFile>> &outputFile) {
|
||||
if (verbosePassExecutions)
|
||||
llvm::errs() << "[ibistool] Will run high-level Ibis flow\n";
|
||||
|
||||
loadHighLevelControlflowTransformsPipeline(pm);
|
||||
loadIbisHighLevelPassPipeline(pm);
|
||||
if (outputFormat != OutputLoweredIbis) {
|
||||
loadHandshakeTransformsPipeline(pm);
|
||||
loadDCTransformsPipeline(pm);
|
||||
if (outputFormat != OutputLoweredIbis)
|
||||
loadLowLevelPassPipeline(pm, module, outputFile);
|
||||
}
|
||||
}
|
||||
|
||||
static void loadIbisLoFlow(
|
||||
PassManager &pm, ModuleOp module,
|
||||
std::optional<std::unique_ptr<llvm::ToolOutputFile>> &outputFile) {
|
||||
if (verbosePassExecutions)
|
||||
llvm::errs() << "[ibistool] Will run low-level Ibis flow\n";
|
||||
loadIbisLowLevelPassPipeline(pm);
|
||||
|
||||
if (outputFormat != OutputLoweredIbis)
|
||||
loadLowLevelPassPipeline(pm, module, outputFile);
|
||||
}
|
||||
|
||||
/// Process a single buffer of the input.
|
||||
static LogicalResult processBuffer(
|
||||
MLIRContext &context, TimingScope &ts, llvm::SourceMgr &sourceMgr,
|
||||
std::optional<std::unique_ptr<llvm::ToolOutputFile>> &outputFile) {
|
||||
// Parse the input.
|
||||
mlir::OwningOpRef<mlir::ModuleOp> module;
|
||||
llvm::sys::TimePoint<> parseStartTime;
|
||||
if (verbosePassExecutions) {
|
||||
llvm::errs() << "[ibistool] Running MLIR parser\n";
|
||||
parseStartTime = llvm::sys::TimePoint<>::clock::now();
|
||||
}
|
||||
auto parserTimer = ts.nest("MLIR Parser");
|
||||
module = parseSourceFile<ModuleOp>(sourceMgr, &context);
|
||||
|
||||
if (!module)
|
||||
return failure();
|
||||
|
||||
if (verbosePassExecutions) {
|
||||
auto elpased = std::chrono::duration<double>(
|
||||
llvm::sys::TimePoint<>::clock::now() - parseStartTime) /
|
||||
std::chrono::seconds(1);
|
||||
llvm::errs() << "[ibistool] -- Done in " << llvm::format("%.3f", elpased)
|
||||
<< " sec\n";
|
||||
}
|
||||
|
||||
// Apply any pass manager command line options.
|
||||
PassManager pm(&context);
|
||||
pm.enableVerifier(verifyPasses);
|
||||
pm.enableTiming(ts);
|
||||
if (failed(applyPassManagerCLOptions(pm)))
|
||||
return failure();
|
||||
|
||||
pm.addPass(createSimpleCanonicalizerPass());
|
||||
if (flowKind == HiIbis)
|
||||
loadIbisHiFlow(pm, module.get(), outputFile);
|
||||
else if (flowKind == LoIbis)
|
||||
loadIbisLoFlow(pm, module.get(), outputFile);
|
||||
|
||||
// Go execute!
|
||||
if (failed(pm.run(module.get())))
|
||||
return failure();
|
||||
if (outputFormat != OutputVerilog || outputFormat == OutputSplitVerilog)
|
||||
module->print((*outputFile)->os());
|
||||
|
||||
return success();
|
||||
|
||||
// We intentionally "leak" the Module into the MLIRContext instead of
|
||||
// deallocating it. There is no need to deallocate it right before process
|
||||
// exit.
|
||||
(void)module.release();
|
||||
return success();
|
||||
}
|
||||
|
||||
/// Process a single split of the input. This allocates a source manager and
|
||||
/// creates a regular or verifying diagnostic handler, depending on whether
|
||||
/// the user set the verifyDiagnostics option.
|
||||
static LogicalResult processInputSplit(
|
||||
MLIRContext &context, TimingScope &ts,
|
||||
std::unique_ptr<llvm::MemoryBuffer> buffer,
|
||||
std::optional<std::unique_ptr<llvm::ToolOutputFile>> &outputFile) {
|
||||
llvm::SourceMgr sourceMgr;
|
||||
sourceMgr.AddNewSourceBuffer(std::move(buffer), llvm::SMLoc());
|
||||
if (!verifyDiagnostics) {
|
||||
SourceMgrDiagnosticHandler sourceMgrHandler(sourceMgr, &context);
|
||||
return processBuffer(context, ts, sourceMgr, outputFile);
|
||||
}
|
||||
|
||||
SourceMgrDiagnosticVerifierHandler sourceMgrHandler(sourceMgr, &context);
|
||||
context.printOpOnDiagnostic(false);
|
||||
(void)processBuffer(context, ts, sourceMgr, outputFile);
|
||||
return sourceMgrHandler.verify();
|
||||
}
|
||||
|
||||
/// Process the entire input provided by the user, splitting it up if the
|
||||
/// corresponding option was specified.
|
||||
static LogicalResult
|
||||
processInput(MLIRContext &context, TimingScope &ts,
|
||||
std::unique_ptr<llvm::MemoryBuffer> input,
|
||||
std::optional<std::unique_ptr<llvm::ToolOutputFile>> &outputFile) {
|
||||
if (!splitInputFile)
|
||||
return processInputSplit(context, ts, std::move(input), outputFile);
|
||||
|
||||
return splitAndProcessBuffer(
|
||||
std::move(input),
|
||||
[&](std::unique_ptr<MemoryBuffer> buffer, raw_ostream &) {
|
||||
return processInputSplit(context, ts, std::move(buffer), outputFile);
|
||||
},
|
||||
llvm::outs());
|
||||
}
|
||||
|
||||
static LogicalResult executeIbistool(MLIRContext &context) {
|
||||
if (allowUnregisteredDialects)
|
||||
context.allowUnregisteredDialects();
|
||||
|
||||
// Create the timing manager we use to sample execution times.
|
||||
DefaultTimingManager tm;
|
||||
applyDefaultTimingManagerCLOptions(tm);
|
||||
auto ts = tm.getRootScope();
|
||||
|
||||
// Set up the input file.
|
||||
std::string errorMessage;
|
||||
auto input = openInputFile(inputFilename, &errorMessage);
|
||||
if (!input) {
|
||||
llvm::errs() << errorMessage << "\n";
|
||||
return failure();
|
||||
}
|
||||
|
||||
std::optional<std::unique_ptr<llvm::ToolOutputFile>> outputFile;
|
||||
if (outputFormat != OutputSplitVerilog) {
|
||||
outputFile.emplace(openOutputFile(outputFilename, &errorMessage));
|
||||
if (!*outputFile) {
|
||||
llvm::errs() << errorMessage << "\n";
|
||||
return failure();
|
||||
}
|
||||
}
|
||||
|
||||
// Process the input.
|
||||
if (failed(processInput(context, ts, std::move(input), outputFile)))
|
||||
return failure();
|
||||
|
||||
// If the result succeeded and we're emitting a file, close it.
|
||||
if (outputFile.has_value())
|
||||
(*outputFile)->keep();
|
||||
|
||||
return success();
|
||||
}
|
||||
|
||||
/// Main driver for ibistool command. This sets up LLVM and MLIR, and parses
|
||||
/// command line options before passing off to 'executeIbistool'. This is set
|
||||
/// up so we can `exit(0)` at the end of the program to avoid teardown of the
|
||||
/// MLIRContext and modules inside of it (reducing compile time).
|
||||
int main(int argc, char **argv) {
|
||||
InitLLVM y(argc, argv);
|
||||
|
||||
// Set the bug report message to indicate users should file issues on
|
||||
// llvm/circt and not llvm/llvm-project.
|
||||
setBugReportMsg(circtBugReportMsg);
|
||||
|
||||
// Hide default LLVM options, other than for this tool.
|
||||
// MLIR options are added below.
|
||||
cl::HideUnrelatedOptions(mainCategory);
|
||||
|
||||
// Register any pass manager command line options.
|
||||
registerMLIRContextCLOptions();
|
||||
registerPassManagerCLOptions();
|
||||
registerDefaultTimingManagerCLOptions();
|
||||
registerAsmPrinterCLOptions();
|
||||
|
||||
// Parse pass names in main to ensure static initialization completed.
|
||||
cl::ParseCommandLineOptions(argc, argv, "CIRCT Ibis tool\n");
|
||||
|
||||
DialectRegistry registry;
|
||||
// Register MLIR dialects.
|
||||
registry.insert<mlir::memref::MemRefDialect>();
|
||||
registry.insert<mlir::func::FuncDialect>();
|
||||
registry.insert<mlir::arith::ArithDialect>();
|
||||
registry.insert<mlir::cf::ControlFlowDialect>();
|
||||
registry.insert<mlir::scf::SCFDialect>();
|
||||
|
||||
// Register MLIR passes.
|
||||
mlir::registerCSEPass();
|
||||
mlir::registerSCCPPass();
|
||||
mlir::registerInlinerPass();
|
||||
mlir::registerCanonicalizerPass();
|
||||
|
||||
// Register CIRCT dialects.
|
||||
registry.insert<hw::HWDialect, comb::CombDialect, seq::SeqDialect,
|
||||
sv::SVDialect, handshake::HandshakeDialect, ibis::IbisDialect,
|
||||
dc::DCDialect, esi::ESIDialect, pipeline::PipelineDialect>();
|
||||
|
||||
// Do the guts of the ibistool process.
|
||||
MLIRContext context(registry);
|
||||
auto result = executeIbistool(context);
|
||||
|
||||
// Use "exit" instead of return'ing to signal completion. This avoids
|
||||
// invoking the MLIRContext destructor, which spends a bunch of time
|
||||
// deallocating memory etc which process exit will do for us.
|
||||
exit(failed(result));
|
||||
}
|
Loading…
Reference in New Issue