[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

This commit is contained in:
Hanchen Ye 2020-09-23 12:39:14 -05:00
parent e06ba37001
commit 9b7c1c1c69
10 changed files with 88 additions and 199 deletions

View File

@ -24,10 +24,9 @@ class HLSCppOp<string mnemonic, list<OpTrait> traits = []> :
Op<HLSCppDialect, mnemonic, traits>;
include "Interfaces.td"
include "Attributes.td"
include "ParamOps.td"
include "PragmaOps.td"
include "StructureOps.td"
#endif // SCALEHLS_DIALECT_HLSCPP_HLSCPP_TD

View File

@ -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

View File

@ -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<IntegerAttr>("unroll_factor").getUInt();
}
unsigned getLoopBound() {
return getAttrOfType<IntegerAttr>("loop_bound").getUInt();
}
unsigned getNonprocLatency() {
return getAttrOfType<IntegerAttr>("nonproc_latency").getUInt();
}
unsigned getIterationLatency() {
return getAttrOfType<IntegerAttr>("iteration_latency").getUInt();
}
unsigned getLatency() {
return getAttrOfType<IntegerAttr>("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

View File

@ -16,7 +16,6 @@ namespace mlir {
namespace scalehls {
namespace hlscpp {
std::unique_ptr<mlir::Pass> createStaticAnalysisPass();
std::unique_ptr<mlir::Pass> createQoREstimationPass();
std::unique_ptr<mlir::Pass> createPragmaDSEPass();

View File

@ -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.

View File

@ -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

View File

@ -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<QoREstimation> {
void runOnOperation() override {
QoREstimator(toolConfig, opLatency).estimateModule(getOperation());
// QoREstimator(toolConfig, opLatency).estimateModule(getOperation());
}
};
} // namespace

View File

@ -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<StaticAnalysis> {
void runOnOperation() {}
};
} // namespace
std::unique_ptr<mlir::Pass> hlscpp::createStaticAnalysisPass() {
return std::make_unique<StaticAnalysis>();
}

View File

@ -164,8 +164,10 @@ public:
AddCFOp, SubCFOp, ImOp, ReOp, CreateComplexOp,
// Special operations.
SelectOp, ConstantOp, CopySignOp, TruncateIOp, ZeroExtendIOp,
SignExtendIOp, IndexCastOp, CallOp, ReturnOp, AssignOp, EndOp>(
[&](auto opNode) -> ResultType {
SignExtendIOp, IndexCastOp, CallOp, ReturnOp, AssignOp, EndOp,
// Pragma operations.
ApplyPragmasOp, PragmaPipelineOp, PragmaUnrollOp,
PragmaArrayPartitionOp>([&](auto opNode) -> ResultType {
return thisCast->visitOp(opNode, args...);
})
.Default([&](auto opNode) -> ResultType {
@ -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.");

View File

@ -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() {