From 9b7c1c1c6932025a94c61d664606d54ff8a96f0b Mon Sep 17 00:00:00 2001 From: Hanchen Ye Date: Wed, 23 Sep 2020 12:39:14 -0500 Subject: [PATCH] [HLSCppDialect] pragma operations come back... eventually I found the analysis+estimate 2-step approach in COMBA is misleading, basically you can't clearly partition the task into two passes (StaticAnalysis and QoREstimation), so that I decide to merge the two into one QoREstimation pass. In this case, there's no need to move around estimated or extracted parameters between different passes, which means we can create an internal data structure in QoREstimation pass for decently holding all parameters, and pragma information will continue to be represented as pragma operation --- include/Dialect/HLSCpp/HLSCpp.td | 3 +- include/Dialect/HLSCpp/Interfaces.td | 7 -- include/Dialect/HLSCpp/ParamOps.td | 144 -------------------------- include/Dialect/HLSCpp/Passes.h | 1 - include/Dialect/HLSCpp/Passes.td | 22 +--- include/Dialect/HLSCpp/PragmaOps.td | 2 - lib/Dialect/HLSCpp/QoREstimation.cpp | 5 +- lib/Dialect/HLSCpp/StaticAnalysis.cpp | 20 ---- lib/EmitHLSCpp/EmitHLSCpp.cpp | 81 ++++++++++++++- test/Dialect/HLSCpp/test_pragma.mlir | 2 +- 10 files changed, 88 insertions(+), 199 deletions(-) delete mode 100644 include/Dialect/HLSCpp/ParamOps.td delete mode 100644 lib/Dialect/HLSCpp/StaticAnalysis.cpp diff --git a/include/Dialect/HLSCpp/HLSCpp.td b/include/Dialect/HLSCpp/HLSCpp.td index e7f407e..f1e120e 100644 --- a/include/Dialect/HLSCpp/HLSCpp.td +++ b/include/Dialect/HLSCpp/HLSCpp.td @@ -24,10 +24,9 @@ class HLSCppOp traits = []> : Op; include "Interfaces.td" - include "Attributes.td" -include "ParamOps.td" +include "PragmaOps.td" include "StructureOps.td" #endif // SCALEHLS_DIALECT_HLSCPP_HLSCPP_TD diff --git a/include/Dialect/HLSCpp/Interfaces.td b/include/Dialect/HLSCpp/Interfaces.td index e37e27e..b57ba9e 100644 --- a/include/Dialect/HLSCpp/Interfaces.td +++ b/include/Dialect/HLSCpp/Interfaces.td @@ -14,11 +14,4 @@ def PragmaOpInterface : OpInterface<"PragmaOpInterface"> { }]; } -def ParamOpInterface : OpInterface<"ParamOpInterface"> { - let description = [{ - This interface indicates the operation represents a group of parameters of - its parent region. - }]; -} - #endif // SCALEHLS_DIALECT_HLSCPP_INTERFACES_TD diff --git a/include/Dialect/HLSCpp/ParamOps.td b/include/Dialect/HLSCpp/ParamOps.td deleted file mode 100644 index 3176b6a..0000000 --- a/include/Dialect/HLSCpp/ParamOps.td +++ /dev/null @@ -1,144 +0,0 @@ -//===-------------------------------------------------------*- tablegen -*-===// -// -//===----------------------------------------------------------------------===// - -#ifndef SCALEHLS_DIALECT_HLSCPP_PARAMOPS_TD -#define SCALEHLS_DIALECT_HLSCPP_PARAMOPS_TD - -//===----------------------------------------------------------------------===// -// FuncParamOp -//===----------------------------------------------------------------------===// - -def FuncParamOp : HLSCppOp<"func_param", [ - ParamOpInterface, - HasParent<"FuncOp"> -]> { - let summary = "Hold function parameters"; - let description = [{ - This hlscpp.func_param operation holds parameters for function region. - }]; - - let arguments = (ins - // Pragma configuration parameters - // BoolAttr : $enable_pipeline, - // PositiveUI32Attr : $initial_interval, - - // Resource utilization parameters - UI32Attr : $lut, - UI32Attr : $dsp, - UI32Attr : $bram - ); -} - -//===----------------------------------------------------------------------===// -// LoopParamOp -//===----------------------------------------------------------------------===// - -def LoopParamOp : HLSCppOp<"loop_param", [ - ParamOpInterface, - HasParent<"AffineForOp"> -]> { - let summary = "Hold loop parameters"; - let description = [{ - This hlscpp.loop_param operation holds parameters for loop region. - }]; - - let arguments = (ins - // Pragma configuration parameters - BoolAttr : $enable_pipeline, - PositiveUI32Attr : $initial_interval, - PositiveUI32Attr : $unroll_factor, - - // Performance parameters - UI32Attr : $loop_bound, - UI32Attr : $nonproc_latency, - UI32Attr : $iteration_latency, - UI32Attr : $latency, - - // Resource utilization parameters - UI32Attr : $lut, - UI32Attr : $dsp, - UI32Attr : $bram - ); - - let extraClassDeclaration = [{ - unsigned getUnrollFactor() { - return getAttrOfType("unroll_factor").getUInt(); - } - - unsigned getLoopBound() { - return getAttrOfType("loop_bound").getUInt(); - } - - unsigned getNonprocLatency() { - return getAttrOfType("nonproc_latency").getUInt(); - } - - unsigned getIterationLatency() { - return getAttrOfType("iteration_latency").getUInt(); - } - - unsigned getLatency() { - return getAttrOfType("latency").getUInt(); - } - }]; -} - -//===----------------------------------------------------------------------===// -// IfParamOp -//===----------------------------------------------------------------------===// - -def IfParamOp : HLSCppOp<"if_param", [ - ParamOpInterface, - HasParent<"AffineIfOp"> -]> { - let summary = "Hold if region parameters"; - let description = [{ - This hlscpp.if_param operation holds parameters for if regions, including - then region and else region (if applicable). - }]; - - let arguments = (ins - // Performance parameters - UI32Attr : $latency, - - // Resource utilization parameters - UI32Attr : $lut, - UI32Attr : $dsp, - UI32Attr : $bram - ); -} - -//===----------------------------------------------------------------------===// -// ArrayParamOp -//===----------------------------------------------------------------------===// - -def ArrayParamOp : HLSCppOp<"array_param", [ - ParamOpInterface -]> { - let summary = "Hold array parameters"; - let description = [{ - This hlscpp.array_param operation holds parameters for arrays. - }]; - - let arguments = (ins - // Pragma configuration parameters - StorageTypeAttr : $storage_type, - PartitionTypeAttr : $partition_type, - PositiveUI32ArrayAttr : $partition_factor, - - // Performance parameters - UI32Attr : $reads, - UI32Attr : $writes, - PositiveUI32Attr : $read_ports, - PositiveUI32Attr : $write_ports, - UI32Attr : $dependency_latency, - UI32Attr : $dependency_distance, - - // Resource utilization parameters - UI32Attr : $lut, - UI32Attr : $bram - ); -} - -#endif // SCALEHLS_DIALECT_HLSCPP_PARAMOPS_TD diff --git a/include/Dialect/HLSCpp/Passes.h b/include/Dialect/HLSCpp/Passes.h index 3d3f814..b9b624e 100644 --- a/include/Dialect/HLSCpp/Passes.h +++ b/include/Dialect/HLSCpp/Passes.h @@ -16,7 +16,6 @@ namespace mlir { namespace scalehls { namespace hlscpp { -std::unique_ptr createStaticAnalysisPass(); std::unique_ptr createQoREstimationPass(); std::unique_ptr createPragmaDSEPass(); diff --git a/include/Dialect/HLSCpp/Passes.td b/include/Dialect/HLSCpp/Passes.td index a85e120..bbc4f98 100644 --- a/include/Dialect/HLSCpp/Passes.td +++ b/include/Dialect/HLSCpp/Passes.td @@ -7,24 +7,12 @@ include "mlir/Pass/PassBase.td" -def StaticAnalysis : Pass<"hlscpp-static-analysis", "ModuleOp"> { - let summary = "Analyze CDFG and generate parameters for QoR estimation"; - let description = [{ - This hlscpp-static-analysis pass will analyze the input CDFG and insert - HLSCpp parameter operations for each function, loop, and other regions. The - extracted parameters will then be passed to QoREstimation for estimating - quality of results. - }]; - - let constructor = "mlir::scalehls::hlscpp::createStaticAnalysisPass()"; -} - def QoREstimation : Pass<"hlscpp-qor-estimation", "ModuleOp"> { let summary = "Estimate the performance and resource utilization"; let description = [{ - This hlscpp-qor-estimation pass will take HLSCpp parameter operations inside - of each function, loop, and other regions into consideration and estimate - performance and resource utilization of the HLS results. + This hlscpp-qor-estimation pass will analyze the input CDFG and pragma + operations (if applied) for estimating performance and resource utilization + of the HLS results. }]; let constructor = "mlir::scalehls::hlscpp::createQoREstimationPass()"; @@ -42,8 +30,8 @@ def QoREstimation : Pass<"hlscpp-qor-estimation", "ModuleOp"> { def PragmaDSE : Pass<"hlscpp-pragma-dse", "ModuleOp"> { let summary = "Optimize pragma configuration of each optimizable region"; let description = [{ - This hlscpp-pragma-dse pass will automatically tune HLS pragma optimization - of each optimizable region for performance and area opt. By calling methods + This hlscpp-pragma-dse pass will automatically tune HLS pragma insertion and + configuration for performance and area optimization. By calling methods provided by hlscpp-qor-estimation, this pass is able to rapidly obtain the QoR estimation of the current design point, and feed it back to the design space exploration engine for an efficient convergence. diff --git a/include/Dialect/HLSCpp/PragmaOps.td b/include/Dialect/HLSCpp/PragmaOps.td index 5c364ea..36133ce 100644 --- a/include/Dialect/HLSCpp/PragmaOps.td +++ b/include/Dialect/HLSCpp/PragmaOps.td @@ -2,8 +2,6 @@ // //===----------------------------------------------------------------------===// -/// This file is deprecated and will be removed in the future. - #ifndef SCALEHLS_DIALECT_HLSCPP_PRAGMAOPS_TD #define SCALEHLS_DIALECT_HLSCPP_PRAGMAOPS_TD diff --git a/lib/Dialect/HLSCpp/QoREstimation.cpp b/lib/Dialect/HLSCpp/QoREstimation.cpp index 317b2bb..4e8e26d 100644 --- a/lib/Dialect/HLSCpp/QoREstimation.cpp +++ b/lib/Dialect/HLSCpp/QoREstimation.cpp @@ -10,6 +10,7 @@ using namespace mlir; using namespace scalehls; using namespace hlscpp; +/* namespace { class QoREstimator { public: @@ -104,10 +105,12 @@ void QoREstimator::estimateModule(ModuleOp module) { } } +*/ + namespace { struct QoREstimation : public QoREstimationBase { void runOnOperation() override { - QoREstimator(toolConfig, opLatency).estimateModule(getOperation()); + // QoREstimator(toolConfig, opLatency).estimateModule(getOperation()); } }; } // namespace diff --git a/lib/Dialect/HLSCpp/StaticAnalysis.cpp b/lib/Dialect/HLSCpp/StaticAnalysis.cpp deleted file mode 100644 index 9c6a5f9..0000000 --- a/lib/Dialect/HLSCpp/StaticAnalysis.cpp +++ /dev/null @@ -1,20 +0,0 @@ -//===------------------------------------------------------------*- C++ -*-===// -// -//===----------------------------------------------------------------------===// - -#include "Dialect/HLSCpp/HLSCpp.h" -#include "Dialect/HLSCpp/Passes.h" - -using namespace mlir; -using namespace scalehls; -using namespace hlscpp; - -namespace { -struct StaticAnalysis : public StaticAnalysisBase { - void runOnOperation() {} -}; -} // namespace - -std::unique_ptr hlscpp::createStaticAnalysisPass() { - return std::make_unique(); -} diff --git a/lib/EmitHLSCpp/EmitHLSCpp.cpp b/lib/EmitHLSCpp/EmitHLSCpp.cpp index 6a94dcc..70f192c 100644 --- a/lib/EmitHLSCpp/EmitHLSCpp.cpp +++ b/lib/EmitHLSCpp/EmitHLSCpp.cpp @@ -164,10 +164,12 @@ public: AddCFOp, SubCFOp, ImOp, ReOp, CreateComplexOp, // Special operations. SelectOp, ConstantOp, CopySignOp, TruncateIOp, ZeroExtendIOp, - SignExtendIOp, IndexCastOp, CallOp, ReturnOp, AssignOp, EndOp>( - [&](auto opNode) -> ResultType { - return thisCast->visitOp(opNode, args...); - }) + SignExtendIOp, IndexCastOp, CallOp, ReturnOp, AssignOp, EndOp, + // Pragma operations. + ApplyPragmasOp, PragmaPipelineOp, PragmaUnrollOp, + PragmaArrayPartitionOp>([&](auto opNode) -> ResultType { + return thisCast->visitOp(opNode, args...); + }) .Default([&](auto opNode) -> ResultType { return thisCast->visitInvalidOp(op, args...); }); @@ -288,6 +290,12 @@ public: HANDLE(ReturnOp); HANDLE(AssignOp); HANDLE(EndOp); + + // Pragma operations. + HANDLE(ApplyPragmasOp); + HANDLE(PragmaPipelineOp); + HANDLE(PragmaUnrollOp); + HANDLE(PragmaArrayPartitionOp); #undef HANDLE }; } // namespace @@ -341,6 +349,12 @@ public: void emitCall(CallOp *op); void emitAssign(AssignOp *op); + /// Pragma operation emitters. + void emitApplyPragmas(ApplyPragmasOp *op); + void emitPragmaPipeline(PragmaPipelineOp *op); + void emitPragmaUnroll(PragmaUnrollOp *op); + void emitPragmaArrayPartition(PragmaArrayPartitionOp *op); + /// Top-level MLIR module emitter. void emitModule(ModuleOp module); @@ -621,6 +635,20 @@ public: using HLSCppVisitorBase::visitOp; + /// Pragma operations. + bool visitOp(ApplyPragmasOp op) { + return emitter.emitApplyPragmas(&op), true; + } + bool visitOp(PragmaPipelineOp op) { + return emitter.emitPragmaPipeline(&op), true; + } + bool visitOp(PragmaUnrollOp op) { + return emitter.emitPragmaUnroll(&op), true; + } + bool visitOp(PragmaArrayPartitionOp op) { + return emitter.emitPragmaArrayPartition(&op), true; + } + private: ModuleEmitter &emitter; }; @@ -1173,6 +1201,51 @@ void ModuleEmitter::emitAssign(AssignOp *op) { emitNestedLoopTail(rank); } +/// Pragma operation emitters. +void ModuleEmitter::emitApplyPragmas(ApplyPragmasOp *op) { + emitBlock(*op->getBody()); + os << "\n"; +} + +void ModuleEmitter::emitPragmaPipeline(PragmaPipelineOp *op) { + indent(); + os << "#pragma HLS pipeline"; + if (op->isOff()) + os << " off\n"; + else { + os << " II=" << op->getII(); + if (op->isRewind()) + os << " rewind"; + if (op->isEnableFlush()) + os << " enable_flush"; + os << "\n"; + } +} + +void ModuleEmitter::emitPragmaUnroll(PragmaUnrollOp *op) { + indent(); + os << "#pragma HLS unroll"; + // TODO: default factor. + os << " factor=" << op->getFactor(); + if (op->isRegion()) + os << " region"; + if (op->isSkipExitCheck()) + os << " skip_exit_check"; + os << "\n"; +} + +void ModuleEmitter::emitPragmaArrayPartition(PragmaArrayPartitionOp *op) { + indent(); + os << "#pragma HLS array_partition"; + os << " variable="; + emitValue(op->getOperand()); + os << " " << op->getPartitionType(); + if (op->getPartitionType() != "complete") + os << " factor=" << op->getFactor(); + os << " dim=" << op->getDim(); + os << "\n\n"; +} + /// C++ component emitters. void ModuleEmitter::emitValue(Value val, unsigned rank, bool isPtr) { assert(!(rank && isPtr) && "should be either an array or a pointer."); diff --git a/test/Dialect/HLSCpp/test_pragma.mlir b/test/Dialect/HLSCpp/test_pragma.mlir index a1619d9..68e4e52 100644 --- a/test/Dialect/HLSCpp/test_pragma.mlir +++ b/test/Dialect/HLSCpp/test_pragma.mlir @@ -1,4 +1,4 @@ -// RUN: scalehls-opt -hlscpp-static-analysis -hlscpp-pragma-dse %s | FileCheck %s +// RUN: scalehls-opt -hlscpp-pragma-dse %s | FileCheck %s // CHECK-LABEL: func @test_pragma() func @test_pragma() {