Skip to content

Commit

Permalink
Remove the layer of structural dataflow abstraction
Browse files Browse the repository at this point in the history
  • Loading branch information
hanchenye committed Feb 23, 2024
1 parent 84f820d commit 146e4f4
Show file tree
Hide file tree
Showing 18 changed files with 232 additions and 1,279 deletions.
63 changes: 19 additions & 44 deletions include/scalehls-c/Dialect/HLS/HLS.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,56 +24,31 @@ MLIR_CAPI_EXPORTED void
mlirSemanticsInitializeBlockArguments(MlirOperation semantics,
const std::vector<MlirValue> &ports);

//===----------------------------------------------------------------------===//
// HLS Dialect Types
//===----------------------------------------------------------------------===//

MLIR_CAPI_EXPORTED bool mlirTypeIsHLSStructType(MlirType type);
MLIR_CAPI_EXPORTED MlirType mlirHLSStructTypeGet(MlirStringRef name,
MlirContext ctx);

MLIR_CAPI_EXPORTED bool mlirTypeIsHLSTypeType(MlirType type);
MLIR_CAPI_EXPORTED MlirType mlirHLSTypeTypeGet(MlirContext ctx);

MLIR_CAPI_EXPORTED bool mlirTypeIsHLSIntParamType(MlirType type);
MLIR_CAPI_EXPORTED MlirType mlirHLSIntParamTypeGet(MlirContext ctx);

MLIR_CAPI_EXPORTED bool mlirTypeIsHLSFloatParamType(MlirType type);
MLIR_CAPI_EXPORTED MlirType mlirHLSFloatParamTypeGet(MlirContext ctx);

MLIR_CAPI_EXPORTED bool mlirTypeIsHLSPortType(MlirType type);
MLIR_CAPI_EXPORTED MlirType mlirHLSPortTypeGet(MlirContext ctx);

MLIR_CAPI_EXPORTED bool mlirTypeIsHLSTaskImplType(MlirType type);
MLIR_CAPI_EXPORTED MlirType mlirHLSTaskImplTypeGet(MlirContext ctx);

MLIR_CAPI_EXPORTED bool mlirTypeIsHLSMemoryKindType(MlirType type);
MLIR_CAPI_EXPORTED MlirType mlirHLSMemoryKindTypeGet(MlirContext ctx);

//===----------------------------------------------------------------------===//
// HLS Dialect Attributes
//===----------------------------------------------------------------------===//

enum class MlirParamKind : uint32_t {
TILE_SIZE = 0,
PARALLEL_SIZE = 1,
IP_TEMPLATE = 2,
TASK_IMPL = 3,
MEMORY_KIND = 4
enum class MlirMemoryKind : uint32_t {
UNKNOWN = 0,
LUTRAM_1P = 1,
LUTRAM_2P = 2,
LUTRAM_S2P = 3,
BRAM_1P = 4,
BRAM_2P = 5,
BRAM_S2P = 6,
BRAM_T2P = 7,
URAM_1P = 8,
URAM_2P = 9,
URAM_S2P = 10,
URAM_T2P = 11,
DRAM = 12
};

MLIR_CAPI_EXPORTED bool mlirAttrIsHLSParamKindAttr(MlirAttribute attr);
MLIR_CAPI_EXPORTED MlirAttribute mlirHLSParamKindAttrGet(MlirContext ctx,
MlirParamKind kind);
MLIR_CAPI_EXPORTED MlirParamKind
mlirHLSParamKindAttrGetValue(MlirAttribute attr);

enum class MlirPortKind : uint32_t { INPUT = 0, OUTPUT = 1, PARAM = 2 };

MLIR_CAPI_EXPORTED bool mlirAttrIsHLSPortKindAttr(MlirAttribute attr);
MLIR_CAPI_EXPORTED MlirAttribute mlirHLSPortKindAttrGet(MlirContext ctx,
MlirPortKind kind);
MLIR_CAPI_EXPORTED MlirPortKind mlirHLSPortKindAttrGetValue(MlirAttribute attr);
MLIR_CAPI_EXPORTED bool mlirAttrIsHLSMemoryKindAttr(MlirAttribute attr);
MLIR_CAPI_EXPORTED MlirAttribute mlirHLSMemoryKindAttrGet(MlirContext ctx,
MlirMemoryKind kind);
MLIR_CAPI_EXPORTED MlirMemoryKind
mlirHLSMemoryKindAttrGetValue(MlirAttribute attr);

#ifdef __cplusplus
}
Expand Down
36 changes: 1 addition & 35 deletions include/scalehls/Dialect/HLS/IR/HLS.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ void setRuntimeAttr(Operation *op);

/// Wrap the operations in the block with dispatch op. Return a nullptr if
/// failed.
DispatchOp dispatchBlock(StringRef name, Block *block,
ScheduleOp scheduleBlock(StringRef name, Block *block,
PatternRewriter &rewriter);

/// Fuse the given operations into a new task. The new task will be created
Expand All @@ -72,9 +72,6 @@ DispatchOp dispatchBlock(StringRef name, Block *block,
TaskOp fuseOpsIntoTask(ArrayRef<Operation *> ops, PatternRewriter &rewriter,
Operation *insertToOp = nullptr);

/// Fuse multiple nodes into a new node.
NodeOp fuseNodeOps(ArrayRef<NodeOp> nodes, PatternRewriter &rewriter);

//===----------------------------------------------------------------------===//
// Analysis Utils
//===----------------------------------------------------------------------===//
Expand All @@ -88,34 +85,6 @@ bool isRamT2P(MemRefType type);
bool isDram(MemRefType type);
bool isUnknown(MemRefType type);

/// Get the consumer/producer nodes of the given buffer expect the given op.
SmallVector<NodeOp> getConsumersExcept(Value buffer, NodeOp except);
SmallVector<NodeOp> getProducersExcept(Value buffer, NodeOp except);
SmallVector<NodeOp> getConsumers(Value buffer);
SmallVector<NodeOp> getProducers(Value buffer);
SmallVector<NodeOp> getDependentConsumers(Value buffer, NodeOp node);

/// Get the nested consumer/producer nodes of the given buffer expect the given
/// node. The corresponding buffer values are also returned.
SmallVector<std::pair<NodeOp, Value>> getNestedConsumersExcept(Value buffer,
NodeOp except);
SmallVector<std::pair<NodeOp, Value>> getNestedProducersExcept(Value buffer,
NodeOp except);
SmallVector<std::pair<NodeOp, Value>> getNestedConsumers(Value buffer);
SmallVector<std::pair<NodeOp, Value>> getNestedProducers(Value buffer);

/// Get the depth of a buffer or stream channel. Note that only if the defining
/// operation of the buffer is not a BufferOp or stream types, the returned
/// result will be 1.
unsigned getBufferDepth(Value memref);

/// Find buffer value or buffer op across the dataflow hierarchy.
Value findBuffer(Value memref);
hls::BufferLikeInterface findBufferOp(Value memref);

/// Check whether the given buffer is external.
bool isExtBuffer(Value memref);

/// Check whether the given use has read/write semantics.
bool isRead(OpOperand &use);
bool isWritten(OpOperand &use);
Expand All @@ -132,9 +101,6 @@ bool isFullyPartitioned(MemRefType memrefType);
int64_t getPartitionFactors(MemRefType memrefType,
SmallVectorImpl<int64_t> *factors = nullptr);

/// The current op or contained ops have effect on external buffers.
bool hasEffectOnExternalBuffer(Operation *op);

} // namespace hls
} // namespace scalehls
} // namespace mlir
Expand Down
17 changes: 0 additions & 17 deletions include/scalehls/Dialect/HLS/IR/HLSAttributes.td
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,6 @@ def IndexArrayAttr : TypedArrayAttrBase<IndexAttr, "index array attribute"> {
let constBuilderCall = "$_builder.getIndexArrayAttr($0)";
}

//===----------------------------------------------------------------------===//
// PortKind Attribute
//===----------------------------------------------------------------------===//

def PortKind: I32EnumAttr<"PortKind", "Kind of a port", [
I32EnumAttrCase<"INPUT", 0, "input">,
I32EnumAttrCase<"OUTPUT", 1, "output">,
I32EnumAttrCase<"PARAM", 2, "param">]> {
let cppNamespace = "mlir::scalehls::hls";
let genSpecializedAttr = 0;
}
def PortKindParam: EnumParameter<PortKind>;
def PortKindAttr: EnumAttr<HLSDialect, PortKind> {
let mnemonic = "port";
let assemblyFormat = "`<` $value `>`";
}

//===----------------------------------------------------------------------===//
// MemoryKind Attribute
//===----------------------------------------------------------------------===//
Expand Down
28 changes: 0 additions & 28 deletions include/scalehls/Dialect/HLS/IR/HLSInterfaces.td
Original file line number Diff line number Diff line change
Expand Up @@ -55,32 +55,4 @@ def BufferLikeInterface : OpInterface<"BufferLikeInterface"> {
];
}

def ContainerLikeInterface : OpInterface<"ContainerLikeInterface"> {
let description = [{
This interface is used to represent containers, including dispatch, task,
schedule, and node.
}];
string cppNamespace = "mlir::scalehls::hls";

let methods = [
InterfaceMethod<"Return body region of the stage",
"mlir::Region &", "getBody", (ins), "return $_op.getBody();">,
InterfaceMethod<"Check whether the stage has hierarchy",
"bool", "hasHierarchy", (ins), [{
return $_op.walk([&](ContainerLikeInterface stage) {
if (stage != $_op)
return WalkResult::interrupt();
return WalkResult::advance();
}).wasInterrupted();
}]>,
InterfaceMethod<"Return whether the value is a stage livein",
"bool", "isLivein", (ins "mlir::Value":$value)>,
InterfaceMethod<"Return the liveins of the stage",
"llvm::SmallVector<mlir::Value>", "getLiveins">,
InterfaceMethod<"Return the internal users of a stage livein",
"llvm::SmallVector<mlir::Operation *>", "getLiveinUsers",
(ins "mlir::Value":$livein)>,
];
}

#endif // SCALEHLS_DIALECT_HLS_HLSINTERFACES_TD
141 changes: 12 additions & 129 deletions include/scalehls/Dialect/HLS/IR/HLSOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -220,13 +220,13 @@ def StreamCastOp : HLSOp<"stream_cast", [Pure,
// Functional Dataflow (FDF) Operations
//===----------------------------------------------------------------------===//

def DispatchOp : HLSOp<"dispatch", [RecursiveMemoryEffects,
def ScheduleOp : HLSOp<"schedule", [RecursiveMemoryEffects,
SingleBlockImplicitTerminator<"YieldOp">,
ParentOneOf<["func::FuncOp", "affine::AffineForOp", "scf::ForOp"]>]> {
let summary = "Represent a dataflow dispatch";
let summary = "Represent a dataflow schedule";
let description = [{
Dispatch op has a transparent region that contains a list of task ops to be
dispatched. This op is designed to organize and manipulate task ops at a
Schedule op has a transparent region that contains a list of task ops to be
scheduleed. This op is designed to organize and manipulate task ops at a
high level and will be lowered to schedule op for dataflow scheduling.
}];

Expand All @@ -242,9 +242,8 @@ def DispatchOp : HLSOp<"dispatch", [RecursiveMemoryEffects,
}];
}

def TaskOp : HLSOp<"task", [DeclareOpInterfaceMethods<ContainerLikeInterface>,
RecursiveMemoryEffects, SingleBlockImplicitTerminator<"YieldOp">,
HasParent<"DispatchOp">]> {
def TaskOp : HLSOp<"task", [RecursiveMemoryEffects, HasParent<"ScheduleOp">,
SingleBlockImplicitTerminator<"YieldOp">]> {
let summary = "Represent a dataflow task";
let description = [{
Task op has a transparent region that contains a list of ops to be executed
Expand All @@ -263,142 +262,26 @@ def TaskOp : HLSOp<"task", [DeclareOpInterfaceMethods<ContainerLikeInterface>,
let extraClassDeclaration = [{
/// Return true if this task op contains nested sub-tasks.
bool hasHierarchy() {
return cast<ContainerLikeInterface>(this->getOperation()).hasHierarchy();
return walk([&](TaskOp task) {
return task != *this ? WalkResult::interrupt() : WalkResult::advance();
}).wasInterrupted();
}

DispatchOp getDispatchOp();
ScheduleOp getScheduleOp();
YieldOp getYieldOp();
}];
}

def YieldOp : HLSOp<"yield", [Pure, ReturnLike, Terminator,
ParentOneOf<["DispatchOp", "TaskOp"]>]> {
let summary = "Terminate and yield results of a dispatch or task op";
ParentOneOf<["ScheduleOp", "TaskOp"]>]> {
let summary = "Terminate and yield results of a schedule or task op";

let arguments = (ins Variadic<AnyType>:$results);
let assemblyFormat = "$results attr-dict `:` type($results)";

let builders = [OpBuilder<(ins), "build($_builder, $_state, std::nullopt);">];
}

//===----------------------------------------------------------------------===//
// Structural Dataflow (SDF) Operations
//===----------------------------------------------------------------------===//

def ScheduleOp : HLSOp<"schedule", [
DeclareOpInterfaceMethods<MemoryEffectsOpInterface>, IsolatedFromAbove,
AffineScope, SingleBlock, NoTerminator,
ParentOneOf<["func::FuncOp", "affine::AffineForOp", "scf::ForOp"]>]> {
let summary = "Represent a dataflow schedule";
let description = [{
Schedule op has an isolated region to contain a list of dataflow node ops to
be scheduled. This op can be explicitly marked as legal when all the
dataflow violations have been resolved and all the nodes has been scheduled.
}];

let arguments = (ins Variadic<AnyType>:$operands, UnitAttr:$isLegal);
let regions = (region SizedRegion<1>:$body);
let assemblyFormat = [{
(`legal` $isLegal^)? (`(` $operands^ `)`)? (`:` type($operands)^)? $body
attr-dict
}];

let hasVerifier = 1;
let hasCanonicalizer = 1;

let extraClassDeclaration = [{
/// FIXME: Check whether the schedule is dependence free.
bool isDependenceFree();

/// Update the signature of the schedule op recursively.
void updateSignatureRecursively();
}];
}

def NodeOp : HLSOp<"node", [DeclareOpInterfaceMethods<MemoryEffectsOpInterface>,
DeclareOpInterfaceMethods<ContainerLikeInterface>, IsolatedFromAbove,
AffineScope, SingleBlock, NoTerminator, AttrSizedOperandSegments,
HasParent<"ScheduleOp">]> {
let summary = "Represent a dataflow node";
let description = [{
Node op has an isolated region to represent the ops contained by the node.
The node can only take buffers or streams as inputs and outputs. Meanwhile,
scalar values can be passed into a node as parameters, which will not be
considered in the dataflow. An attribute "inputTaps" is used to represent
the level of buffer or stream channel tap of each input.
}];

let arguments = (ins Variadic<AnyBufferOrStream>:$inputs,
Variadic<AnyBufferOrStream>:$outputs, Variadic<AnyType>:$params,
I32ArrayAttr:$inputTaps, OptionalAttr<I32Attr>:$level);
let regions = (region SizedRegion<1>:$body);

let assemblyFormat = [{
`(` $inputs `)` `->` `(` $outputs `)` (`[` $params^ `]`)? `:`
functional-type($inputs, $outputs) (`[` type($params)^ `]`)? $body attr-dict
}];

let hasVerifier = 1;
let hasCanonicalizer = 1;
let builders = [
OpBuilder<(ins "mlir::ValueRange":$inputs, "mlir::ValueRange":$outputs,
"mlir::ValueRange":$params, "ArrayRef<unsigned>":$inputTaps,
"mlir::IntegerAttr":$level), [{
auto newInputTaps = SmallVector<int32_t>(
llvm::map_range(inputTaps, [](unsigned a) { return (int32_t)a; }));
build($_builder, $_state, inputs, outputs, params,
$_builder.getI32ArrayAttr(newInputTaps), level);
}]>,

OpBuilder<(ins "mlir::ValueRange":$inputs, "mlir::ValueRange":$outputs,
"mlir::ValueRange":$params, "ArrayRef<unsigned>":$inputTaps), [{
build($_builder, $_state, inputs, outputs, params, inputTaps, nullptr);
}]>,
OpBuilder<(ins "mlir::ValueRange":$inputs, "mlir::ValueRange":$outputs,
"ArrayRef<unsigned>":$inputTaps), [{
build($_builder, $_state, inputs, outputs, ValueRange(), inputTaps);
}]>,

OpBuilder<(ins "mlir::ValueRange":$inputs, "mlir::ValueRange":$outputs,
"mlir::ValueRange":$params), [{
build($_builder, $_state, inputs, outputs, params,
SmallVector<unsigned>(inputs.size(), 0));
}]>,
OpBuilder<(ins "mlir::ValueRange":$inputs, "mlir::ValueRange":$outputs),
"build($_builder, $_state, inputs, outputs, ValueRange());">
];

let extraClassDeclaration = [{
/// Get input taps.
void setInputTap(unsigned idx, unsigned tap);
unsigned getInputTap(unsigned idx);
SmallVector<unsigned> getInputTapsAsInt();

/// Get the number of inputs, outputs, and params.
unsigned getNumInputs();
unsigned getNumOutputs();
unsigned getNumParams();

/// Get the type of operand: input, output, or param.
PortKind getPortKind(OpOperand &operand);
PortKind getPortKind(unsigned operandIdx);

/// Get the input, output, and param arguments.
iterator_range<Block::args_iterator> getInputArgs();
iterator_range<Block::args_iterator> getOutputArgs();
iterator_range<Block::args_iterator> getParamArgs();

bool hasHierarchy() {
return cast<ContainerLikeInterface>(this->getOperation()).hasHierarchy();
}

/// Update the signature of the node op recursively.
void updateSignatureRecursively();

ScheduleOp getScheduleOp();
}];
}

def BufferOp : HLSOp<"buffer", [DeclareOpInterfaceMethods<BufferLikeInterface>,
DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
let summary = "Represent a dataflow buffer";
Expand Down
3 changes: 1 addition & 2 deletions include/scalehls/Dialect/HLS/Transforms/Passes.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ std::unique_ptr<Pass> createReduceTensorToStreamPass();
std::unique_ptr<Pass> createMaterializeStreamPass(bool enablePacking = true);
std::unique_ptr<Pass> createScalarizeStreamPass();

std::unique_ptr<Pass> createCreateDataflowPass();
std::unique_ptr<Pass> createLowerDataflowPass();
std::unique_ptr<Pass> createScheduleDataflowPass();
std::unique_ptr<Pass> createConvertDataflowToFuncPass();

std::unique_ptr<Pass> createApplyTransformPatternPass();
Expand Down
Loading

0 comments on commit 146e4f4

Please sign in to comment.