[Handshake] Add StaticLogic Dialect and Pipeline Operation (#62)

* add pipeline operation

* add staticlogic dialect lib

* add standard-to-staticlogic pass

* update standard-to-staticlogic pass

* update conversion pass

* complete initial design of -create-pipeline pass

* format fixed; add testcase for -create-pipeline pass

* update references

* update pipeline operation defination; update standard-to-pipeline pass; update testcase

* add endline

* update pipeline operation and lowering pass: isolate from above, but not single-producer single-consumer; update test case.
This commit is contained in:
Hanchen Ye 2020-08-04 00:29:40 -05:00 committed by GitHub
parent d284b0dddc
commit 9ab7d667f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 384 additions and 1 deletions

View File

@ -0,0 +1,19 @@
//===- StandardToStaticLogic.h ----------------------------------*- C++ -*-===//
//
// Copyright 2020 The CIRCT Authors.
//
// 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_CONVERSION_STANDARDTOSTATICLOGIC_H_
#define CIRCT_CONVERSION_STANDARDTOSTATICLOGIC_H_
namespace circt {
namespace staticlogic {
void registerStandardToStaticLogicPasses();
} // namespace staticlogic
} // namespace circt
#endif // CIRCT_CONVERSION_STANDARDTOSTATICLOGIC_H_

View File

@ -2,3 +2,4 @@ add_subdirectory(FIRRTL)
add_subdirectory(Handshake)
add_subdirectory(LLHD)
add_subdirectory(RTL)
add_subdirectory(StaticLogic)

View File

@ -0,0 +1,3 @@
add_mlir_dialect(StaticLogic staticlogic)
set(LLVM_TARGET_DEFINITIONS StaticLogic.td)

View File

@ -0,0 +1,44 @@
//===- StaticLogic.h - StaticLogic Definitions ------------------*- C++ -*-===//
//
// Copyright 2020 The CIRCT Authors.
//
// 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_STATICLOGIC_OPS_H_
#define CIRCT_STATICLOGIC_OPS_H_
#include "mlir/IR/Attributes.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/Dialect.h"
#include "mlir/IR/Function.h"
#include "mlir/IR/OpDefinition.h"
#include "mlir/IR/OpImplementation.h"
#include "mlir/IR/Operation.h"
#include "mlir/IR/RegionKindInterface.h"
#include "mlir/IR/StandardTypes.h"
#include "mlir/IR/TypeSupport.h"
#include "mlir/IR/Types.h"
#include "mlir/Interfaces/SideEffectInterfaces.h"
#include "mlir/Pass/Pass.h"
namespace circt {
namespace staticlogic {
using namespace mlir;
class StaticLogicDialect : public Dialect {
public:
StaticLogicDialect(MLIRContext *context);
static StringRef getDialectNamespace() { return "staticlogic"; }
};
#define GET_OP_CLASSES
#include "circt/Dialect/StaticLogic/StaticLogic.h.inc"
} // namespace staticlogic
} // namespace circt
#endif // CIRCT_STATICLOGIC_OPS_H_

View File

@ -0,0 +1,73 @@
//===- StaticLogic.td - StaticLogic Definitions ------------*- tablegen -*-===//
//
// Copyright 2020 The CIRCT Authors.
//
// 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
//===----------------------------------------------------------------------===//
#ifdef STATICLOGIC_OPS
#else
#define STATICLOGIC_OPS
#ifdef OP_BASE
#else
include "mlir/IR/OpBase.td"
#endif // OP_BASE
include "mlir/IR/SymbolInterfaces.td"
include "mlir/IR/RegionKindInterface.td"
include "mlir/Interfaces/CallInterfaces.td"
include "mlir/Interfaces/SideEffectInterfaces.td"
def StaticLogic_Dialect : Dialect {
let name = "staticlogic";
let cppNamespace = "";
}
def PipelineOp : Op<StaticLogic_Dialect, "pipeline", [NoSideEffect]> {
let summary = "pipeline operation";
let description = [{
The "staticlogic.pipeline" operation represents a statically scheduled
pipeline stucture which contains several MLIR blocks. Each MLIR block is
corresponding to a pipeline stage.
}];
let arguments = (ins Variadic<AnyType>);
let results = (outs Variadic<AnyType>);
let regions = (region AnyRegion: $body);
let skipDefaultBuilders = 1;
let builders = [OpBuilder<"OpBuilder &odsBuilder, OperationState &odsState, "
"ValueRange operands, ValueRange results", [{
SmallVector<Type, 4> argTypes;
for (auto value : operands)
argTypes.push_back(value.getType());
SmallVector<Type, 4> resultTypes;
for (auto value : results)
resultTypes.push_back(value.getType());
Region *bodyRegion = odsState.addRegion();
Block *body = new Block();
bodyRegion->push_back(body);
body->addArguments(argTypes);
odsState.addOperands(operands);
odsState.addTypes(resultTypes);
}]>];
}
def ReturnOp : Op<StaticLogic_Dialect, "return", [Terminator]> {
let summary = "StaticLogic dialect return.";
let description = [{
The "staticlogic.return" operation represents a terminator of a statically
scheduled module, which is similar to a standard return operation.
}];
let arguments = (ins Variadic<AnyType>: $operands);
}
#endif // STATICLOGIC_OPS

View File

@ -1,3 +1,4 @@
add_subdirectory(StandardToHandshake)
add_subdirectory(HandshakeToFIRRTL)
add_subdirectory(LLHDToLLVM)
add_subdirectory(StandardToStaticLogic)

View File

@ -8,7 +8,7 @@
// =============================================================================
#include "circt/Conversion/StandardToHandshake/StandardToHandshake.h"
#include "circt/Dialect/StaticLogic/StaticLogic.h"
#include "circt/Dialect/Handshake/HandshakeOps.h"
#include "mlir/Dialect/StandardOps/IR/Ops.h"
#include "mlir/IR/Builders.h"

View File

@ -0,0 +1,14 @@
add_mlir_library(MLIRStandardToStaticLogic
StandardToStaticLogic.cpp
ADDITIONAL_HEADER_DIRS
${MLIR_MAIN_INCLUDE_DIR}/mlir/Conversion/StandardToStaticLogic
LINK_LIBS PUBLIC
MLIRIR
MLIRPass
MLIRStandardOps
MLIRSupport
MLIRTransforms
MLIRStaticLogicOps
)

View File

@ -0,0 +1,122 @@
//===- Ops.h - StaticLogic MLIR Operations ----------------------*- C++ -*-===//
//
// Copyright 2020 The CIRCT Authors.
//
// 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/Conversion/StandardToStaticLogic/StandardToStaticLogic.h"
#include "circt/Dialect/StaticLogic/StaticLogic.h"
#include "mlir/Dialect/StandardOps/IR/Ops.h"
using namespace mlir;
using namespace circt;
using namespace staticlogic;
using namespace std;
using valueVector = SmallVector<Value, 4>;
valueVector getPipelineArgs(Block &block) {
valueVector arguments;
for (auto &op : block) {
if (op.isKnownNonTerminator()) {
for (auto operand : op.getOperands()) {
if (operand.getKind() == Value::Kind::BlockArgument) {
// Add only unique uses
if (std::find(arguments.begin(), arguments.end(), operand) ==
arguments.end())
arguments.push_back(operand);
} else if (operand.getDefiningOp()->getBlock() != &block) {
// Add only unique uses
if (std::find(arguments.begin(), arguments.end(), operand) ==
arguments.end())
arguments.push_back(operand);
}
}
}
}
return arguments;
}
valueVector getPipelineResults(Block &block) {
SmallVector<Value, 4> results;
for (auto &op : block) {
for (auto result : op.getResults()) {
bool isResult = false;
for (auto user : result.getUsers()) {
if (user->getBlock() != &block || user->isKnownTerminator()) {
isResult = true;
break;
}
}
if (isResult)
results.push_back(result);
}
}
return results;
}
static void createPipeline(mlir::FuncOp f, OpBuilder &builder) {
for (Block &block : f) {
if (block.front().isKnownNonTerminator()) {
auto arguments = getPipelineArgs(block);
auto results = getPipelineResults(block);
builder.setInsertionPoint(&block.back());
builder.create<staticlogic::ReturnOp>(f.getLoc(), ValueRange(results));
// Create pipeline operation, and move all operations except terminator
// into the pipeline.
builder.setInsertionPoint(&block.front());
auto pipeline = builder.create<staticlogic::PipelineOp>(
f.getLoc(), ValueRange(arguments), ValueRange(results));
auto &body = pipeline.getRegion().front();
body.getOperations().splice(body.getOperations().begin(),
block.getOperations(), ++block.begin(),
--block.end());
// Reconnect arguments of the pipeline operation.
unsigned argIdx = 0;
for (auto value : arguments) {
value.replaceUsesWithIf(
body.getArgument(argIdx),
function_ref<bool(OpOperand &)>([&body](OpOperand &use) -> bool {
return use.getOwner()->getBlock() == &body;
}));
argIdx += 1;
}
// Reconnect results of the pipeline operation.
unsigned resultIdx = 0;
for (auto value : results) {
value.replaceUsesWithIf(
pipeline.getResult(resultIdx),
function_ref<bool(OpOperand &)>([&body](OpOperand &use) -> bool {
return use.getOwner()->getBlock() != &body;
}));
resultIdx += 1;
}
}
}
}
namespace {
struct CreatePipelinePass
: public PassWrapper<CreatePipelinePass, OperationPass<mlir::FuncOp>> {
void runOnOperation() override {
mlir::FuncOp f = getOperation();
auto builder = OpBuilder(f.getContext());
createPipeline(f, builder);
}
};
} // namespace
void staticlogic::registerStandardToStaticLogicPasses() {
PassRegistration<CreatePipelinePass>(
"create-pipeline", "Create StaticLogic pipeline operations.");
}

View File

@ -2,3 +2,4 @@ add_subdirectory(FIRRTL)
add_subdirectory(Handshake)
add_subdirectory(LLHD)
add_subdirectory(RTL)
add_subdirectory(StaticLogic)

View File

@ -0,0 +1,20 @@
set(LLVM_OPTIONAL_SOURCES
DialectRegistration.cpp
mlir_std_runner.cpp
Ops.cpp
)
add_mlir_dialect_library(MLIRStaticLogicOps
Ops.cpp
ADDITIONAL_HEADER_DIRS
${PROJECT_SOURCE_DIR}/include
${PROJECT_BINARY_DIR}/include
LINK_LIBS PUBLIC
MLIRStandardOps
MLIRIR
DEPENDS
MLIRStaticLogicIncGen
)

View File

@ -0,0 +1,30 @@
//===- Ops.h - StaticLogic MLIR Operations ----------------------*- C++ -*-===//
//
// Copyright 2020 The CIRCT Authors.
//
// 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/StaticLogic/StaticLogic.h"
#include "mlir/IR/FunctionImplementation.h"
using namespace mlir;
using namespace circt;
using namespace circt::staticlogic;
namespace circt {
namespace staticlogic {
#define GET_OP_CLASSES
#include "circt/Dialect/StaticLogic/StaticLogic.cpp.inc"
} // namespace staticlogic
} // namespace circt
StaticLogicDialect::StaticLogicDialect(MLIRContext *context)
: Dialect(getDialectNamespace(), context) {
addOperations<
#define GET_OP_LIST
#include "circt/Dialect/StaticLogic/StaticLogic.cpp.inc"
>();
}

View File

@ -0,0 +1,49 @@
// RUN: circt-opt -create-pipeline %s | FileCheck %s
// CHECK: module {
// CHECK-LABEL: func @simple_loop() {
// CHECK: br ^bb1
// CHECK: ^bb1: // pred: ^bb0
// CHECK: %[[VAL_0:.*]]:2 = "staticlogic.pipeline"() ( {
// CHECK: %c1 = constant 1 : index
// CHECK: %c42 = constant 42 : index
// CHECK: "staticlogic.return"(%c1, %c42) : (index, index) -> ()
// CHECK: }) : () -> (index, index)
// CHECK: br ^bb2(%[[VAL_0:.*]]#0 : index)
// CHECK: ^bb2(%[[VAL_1:.*]]: index): // 2 preds: ^bb1, ^bb3
// CHECK: %[[VAL_2:.*]] = "staticlogic.pipeline"(%[[VAL_1:.*]], %[[VAL_0:.*]]#1) ( {
// CHECK: ^bb0(%[[ARG_0:.*]]: index, %[[ARG_1:.*]]: index): // no predecessors
// CHECK: %[[TMP_0:.*]] = cmpi "slt", %[[ARG_0:.*]], %[[ARG_1:.*]] : index
// CHECK: "staticlogic.return"(%[[TMP_0:.*]]) : (i1) -> ()
// CHECK: }) : (index, index) -> i1
// CHECK: cond_br %[[VAL_2:.*]], ^bb3, ^bb4
// CHECK: ^bb3: // pred: ^bb2
// CHECK: %[[VAL_3:.*]] = "staticlogic.pipeline"(%[[VAL_1:.*]]) ( {
// CHECK: ^bb0(%[[ARG_0:.*]]: index): // no predecessors
// CHECK: %c1 = constant 1 : index
// CHECK: %[[TMP_1:.*]] = addi %[[ARG_0:.*]], %c1 : index
// CHECK: "staticlogic.return"(%[[TMP_1:.*]]) : (index) -> ()
// CHECK: }) : (index) -> index
// CHECK: br ^bb2(%[[VAL_3:.*]] : index)
// CHECK: ^bb4: // pred: ^bb2
// CHECK: return
// CHECK: }
// CHECK: }
func @simple_loop() {
^bb0:
br ^bb1
^bb1: // pred: ^bb0
%c1 = constant 1 : index
%c42 = constant 42 : index
br ^bb2(%c1 : index)
^bb2(%0: index): // 2 preds: ^bb1, ^bb3
%1 = cmpi "slt", %0, %c42 : index
cond_br %1, ^bb3, ^bb4
^bb3: // pred: ^bb2
%c1_0 = constant 1 : index
%2 = addi %0, %c1_0 : index
br ^bb2(%2 : index)
^bb4: // pred: ^bb2
return
}

View File

@ -9,9 +9,11 @@ llvm_update_compile_flags(circt-opt)
target_link_libraries(circt-opt
PRIVATE
MLIRFIRRTL
MLIRStaticLogicOps
MLIRHandshakeOps
MLIRRTL
MLIRStandardToHandshake
MLIRStandardToStaticLogic
MLIRHandshakeToFIRRTL
MLIRLLHD
MLIRLLHDTransforms

View File

@ -8,8 +8,10 @@
#include "circt/Conversion/HandshakeToFIRRTL/HandshakeToFIRRTL.h"
#include "circt/Conversion/LLHDToLLVM/LLHDToLLVM.h"
#include "circt/Conversion/StandardToHandshake/StandardToHandshake.h"
#include "circt/Conversion/StandardToStaticLogic/StandardToStaticLogic.h"
#include "circt/Dialect/FIRRTL/Dialect.h"
#include "circt/Dialect/Handshake/HandshakeOps.h"
#include "circt/Dialect/StaticLogic/StaticLogic.h"
#include "circt/Dialect/LLHD/IR/LLHDDialect.h"
#include "circt/Dialect/LLHD/Transforms/Passes.h"
#include "circt/Dialect/RTL/Dialect.h"
@ -88,6 +90,8 @@ int main(int argc, char **argv) {
firrtl::registerFIRRTLPasses();
registerDialect<handshake::HandshakeOpsDialect>();
registerDialect<staticlogic::StaticLogicDialect>();
staticlogic::registerStandardToStaticLogicPasses();
handshake::registerStandardToHandshakePasses();
handshake::registerHandshakeToFIRRTLPasses();