[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:
parent
e06ba37001
commit
9b7c1c1c69
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>();
|
||||
}
|
|
@ -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.");
|
||||
|
|
|
@ -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() {
|
||||
|
|
Loading…
Reference in New Issue