[EmitHLSCpp] support to emit line number and schedule information; [HLSCpp] update all code related to storage_type attribute to align with Vivado 2019.1 settings
This commit is contained in:
parent
bf6c17f1ab
commit
53550db33a
|
@ -40,12 +40,12 @@ $ # Benchmark generation, dataflow-level optimization, and bufferization.
|
|||
$ benchmark-gen -type "cnn" -config "config/cnn-config.ini" -number 1 \
|
||||
| scalehls-opt -legalize-dataflow -split-function \
|
||||
-hlskernel-bufferize -hlskernel-to-affine -func-bufferize -canonicalize
|
||||
$
|
||||
|
||||
$ # HLSKernel lowering, loop-level and pragma-level optimizations, and performance estimation.
|
||||
$ scalehls-opt test/Conversion/HLSKernelToAffine/test_gemm.mlir -hlskernel-to-affine \
|
||||
-affine-loop-perfection -remove-var-loop-bound -partial-affine-loop-tile="tile-level=1 tile-size=4" \
|
||||
-convert-to-hlscpp="top-function=test_gemm" -loop-pipelining="pipeline-level=1" \
|
||||
-store-op-forward -simplify-memref-access -array-partition -canonicalize \
|
||||
-store-op-forward -simplify-memref-access -array-partition -cse -canonicalize \
|
||||
-qor-estimation="target-spec=config/target-spec.ini"
|
||||
|
||||
$ # HLS C++ code generation.
|
||||
|
|
|
@ -33,22 +33,32 @@ def PartitionTypeArrayAttr : TypedArrayAttrBase<PartitionTypeAttr, ""> {}
|
|||
// Pragma bind_storage Attributes
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
def StorageImplAttr : StrEnumAttr<"StorageImpl", "", [
|
||||
StrEnumAttrCase<"bram", 0>,
|
||||
StrEnumAttrCase<"lutram", 1>,
|
||||
StrEnumAttrCase<"uram", 2>,
|
||||
StrEnumAttrCase<"srl", 3>
|
||||
]> {
|
||||
let cppNamespace = "::mlir::scalehls::hlscpp";
|
||||
}
|
||||
// def StorageImplAttr : StrEnumAttr<"StorageImpl", "", [
|
||||
// StrEnumAttrCase<"bram", 0>,
|
||||
// StrEnumAttrCase<"lutram", 1>,
|
||||
// StrEnumAttrCase<"uram", 2>,
|
||||
// StrEnumAttrCase<"srl", 3>
|
||||
// ]> {
|
||||
// let cppNamespace = "::mlir::scalehls::hlscpp";
|
||||
// }
|
||||
|
||||
// def StorageTypeAttr : StrEnumAttr<"StorageType", "", [
|
||||
// StrEnumAttrCase<"fifo", 0>,
|
||||
// StrEnumAttrCase<"ram_1p", 1>,
|
||||
// StrEnumAttrCase<"ram_1wnr", 2>,
|
||||
// StrEnumAttrCase<"ram_2p", 3>,
|
||||
// StrEnumAttrCase<"ram_s2p", 4>,
|
||||
// StrEnumAttrCase<"ram_t2p", 5>
|
||||
// ]> {
|
||||
// let cppNamespace = "::mlir::scalehls::hlscpp";
|
||||
// }
|
||||
|
||||
// Currently we only support bram/lut based memory instance.
|
||||
def StorageTypeAttr : StrEnumAttr<"StorageType", "", [
|
||||
StrEnumAttrCase<"fifo", 0>,
|
||||
StrEnumAttrCase<"ram_1p", 1>,
|
||||
StrEnumAttrCase<"ram_1wnr", 2>,
|
||||
StrEnumAttrCase<"ram_2p", 3>,
|
||||
StrEnumAttrCase<"ram_s2p", 4>,
|
||||
StrEnumAttrCase<"ram_t2p", 5>
|
||||
StrEnumAttrCase<"ram_1p_bram", 0>,
|
||||
StrEnumAttrCase<"ram_2p_bram", 1>,
|
||||
StrEnumAttrCase<"ram_s2p_bram", 2>,
|
||||
StrEnumAttrCase<"ram_t2p_bram", 3>
|
||||
]> {
|
||||
let cppNamespace = "::mlir::scalehls::hlscpp";
|
||||
}
|
||||
|
|
|
@ -33,15 +33,15 @@ def ArrayOp : HLSCppOp<"array", [SameOperandsAndResultType]> {
|
|||
// Interface-related attributes.
|
||||
DefaultValuedAttr<BoolAttr, "false"> : $interface,
|
||||
DefaultValuedAttr<InterfaceModeAttr, "bram"> : $interface_mode,
|
||||
DefaultValuedAttr<PositiveUI32Attr, "1024"> : $interface_depth,
|
||||
DefaultValuedAttr<PositiveUI32Attr, "1"> : $interface_depth,
|
||||
|
||||
// BindStorage-related attributes.
|
||||
DefaultValuedAttr<BoolAttr, "false"> : $storage,
|
||||
DefaultValuedAttr<StorageTypeAttr, "ram_2p"> : $storage_type,
|
||||
DefaultValuedAttr<StorageImplAttr, "bram"> : $storage_impl,
|
||||
DefaultValuedAttr<StorageTypeAttr, "ram_1p_bram"> : $storage_type,
|
||||
|
||||
// ArrayPartition-related attributes.
|
||||
DefaultValuedAttr<BoolAttr, "false"> : $partition,
|
||||
DefaultValuedAttr<PositiveUI32Attr, "1"> : $partition_num,
|
||||
DefaultValuedAttr<PartitionTypeArrayAttr, "{}"> : $partition_type,
|
||||
DefaultValuedAttr<PositiveUI32ArrayAttr, "{}"> : $partition_factor
|
||||
);
|
||||
|
|
|
@ -245,9 +245,8 @@ unsigned HLSCppEstimator::getLoadStoreSchedule(Operation *op, unsigned begin) {
|
|||
setAttrValue(op, "partition_index", partitionIdx);
|
||||
|
||||
auto arrayOp = getArrayOp(op);
|
||||
auto partitionNum =
|
||||
max((unsigned)1, getUIntAttrValue(arrayOp, "partition_num"));
|
||||
auto storageType = getStrAttrValue(arrayOp, "storage_type");
|
||||
auto partitionNum = arrayOp.partition_num();
|
||||
auto storageType = arrayOp.storage_type();
|
||||
|
||||
// Try to avoid memory port violation until a legal schedule is found. Since
|
||||
// an infinite length schedule cannot be generated, this while loop can be
|
||||
|
@ -265,15 +264,16 @@ unsigned HLSCppEstimator::getLoadStoreSchedule(Operation *op, unsigned begin) {
|
|||
unsigned wrPort = 0;
|
||||
unsigned rdwrPort = 0;
|
||||
|
||||
if (storageType == "ram_s2p")
|
||||
rdPort = 1, wrPort = 1;
|
||||
else if (storageType == "ram_2p" || storageType == "ram_t2p")
|
||||
rdwrPort = 2;
|
||||
else if (storageType == "ram_1p")
|
||||
if (storageType == "ram_1p_bram")
|
||||
rdwrPort = 1;
|
||||
else {
|
||||
else if (storageType == "ram_2p_bram")
|
||||
rdPort = 1, rdwrPort = 1;
|
||||
else if (storageType == "ram_s2p_bram")
|
||||
rdPort = 1, wrPort = 1;
|
||||
else if (storageType == "ram_t2p_bram")
|
||||
rdwrPort = 2;
|
||||
else
|
||||
rdwrPort = 2;
|
||||
}
|
||||
|
||||
memPort.push_back(PortInfo(rdPort, wrPort, rdwrPort));
|
||||
}
|
||||
|
@ -346,7 +346,6 @@ unsigned HLSCppEstimator::getLoadStoreSchedule(Operation *op, unsigned begin) {
|
|||
// else
|
||||
// minII = getUIntAttrValue(op, "schedule_end") -
|
||||
// getUIntAttrValue(op, "schedule_begin");
|
||||
|
||||
// II = max(II, minII);
|
||||
// });
|
||||
// return II;
|
||||
|
@ -359,9 +358,8 @@ unsigned HLSCppEstimator::getResMinII(LoadStoresMap &map) {
|
|||
for (auto &pair : map) {
|
||||
auto arrayOp = getArrayOp(pair.first);
|
||||
// Partition number should at least be 1.
|
||||
auto partitionNum =
|
||||
max((unsigned)1, getUIntAttrValue(arrayOp, "partition_num"));
|
||||
auto storageType = getStrAttrValue(arrayOp, "storage_type");
|
||||
auto partitionNum = arrayOp.partition_num();
|
||||
auto storageType = arrayOp.storage_type();
|
||||
|
||||
SmallVector<unsigned, 16> readNum;
|
||||
SmallVector<unsigned, 16> writeNum;
|
||||
|
@ -376,12 +374,15 @@ unsigned HLSCppEstimator::getResMinII(LoadStoresMap &map) {
|
|||
auto partitionIdx = getIntAttrValue(op, "partition_index");
|
||||
if (partitionIdx == -1) {
|
||||
unsigned accessNum = 2;
|
||||
if (storageType == "ram_s2p")
|
||||
if (storageType == "ram_1p_bram")
|
||||
accessNum = 1;
|
||||
else if (storageType == "ram_1p")
|
||||
else if (storageType == "ram_2p_bram")
|
||||
accessNum = 1;
|
||||
else if (storageType == "ram_2p" || storageType == "ram_t2p" ||
|
||||
storageType == "")
|
||||
else if (storageType == "ram_s2p_bram")
|
||||
accessNum = 1;
|
||||
else if (storageType == "ram_t2p_bram")
|
||||
accessNum = 1;
|
||||
else
|
||||
accessNum = 2;
|
||||
|
||||
// The rationale here is an undetermined partition access will introduce
|
||||
|
@ -401,15 +402,15 @@ unsigned HLSCppEstimator::getResMinII(LoadStoresMap &map) {
|
|||
}
|
||||
|
||||
unsigned minII = 1;
|
||||
if (storageType == "ram_s2p")
|
||||
if (storageType == "ram_s2p_bram")
|
||||
minII = max({minII, *std::max_element(readNum.begin(), readNum.end()),
|
||||
*std::max_element(writeNum.begin(), writeNum.end())});
|
||||
|
||||
else if (storageType == "ram_1p")
|
||||
else if (storageType == "ram_1p_bram")
|
||||
for (unsigned i = 0, e = partitionNum; i < e; ++i)
|
||||
minII = max(minII, readNum[i] + writeNum[i]);
|
||||
|
||||
else if (storageType == "ram_2p" || storageType == "ram_t2p" ||
|
||||
else if (storageType == "ram_2p_bram" || storageType == "ram_t2p_bram" ||
|
||||
storageType == "")
|
||||
for (unsigned i = 0, e = partitionNum; i < e; ++i)
|
||||
minII = max(minII, (readNum[i] + writeNum[i] + 1) / 2);
|
||||
|
@ -672,7 +673,7 @@ void HLSCppEstimator::estimateFunc() {
|
|||
// setScheduleValue(op, latency - end, latency - begin);
|
||||
// });
|
||||
} else
|
||||
setAttrValue(func, "latency", -1);
|
||||
setAttrValue(func, "latency", std::string("unknown"));
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -86,7 +86,7 @@ void ConvertToHLSCpp::runOnOperation() {
|
|||
// an AssignOp, it will always not be annotated as interface. This
|
||||
// is acceptable because AssignOp is only used to handle some weird
|
||||
// corner cases that rarely happen.
|
||||
if (!arrayOp.getAttr("interface") && func.getName() == topFunction) {
|
||||
if (!arrayOp.interface() && func.getName() == topFunction) {
|
||||
// Only if when the array is an block arguments or a returned
|
||||
// value, it will be annotated as interface.
|
||||
bool interfaceFlag =
|
||||
|
@ -99,10 +99,13 @@ void ConvertToHLSCpp::runOnOperation() {
|
|||
} else
|
||||
arrayOp.setAttr("interface", builder.getBoolAttr(false));
|
||||
|
||||
if (!arrayOp.getAttr("storage"))
|
||||
arrayOp.setAttr("storage", builder.getBoolAttr(false));
|
||||
if (!arrayOp.storage()) {
|
||||
arrayOp.setAttr("storage", builder.getBoolAttr(true));
|
||||
arrayOp.setAttr("storage_type",
|
||||
builder.getStringAttr("ram_1p_bram"));
|
||||
}
|
||||
|
||||
if (!arrayOp.getAttr("partition"))
|
||||
if (!arrayOp.partition())
|
||||
arrayOp.setAttr("partition", builder.getBoolAttr(false));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -210,6 +210,7 @@ private:
|
|||
void emitArrayDecl(Value array);
|
||||
unsigned emitNestedLoopHead(Value val);
|
||||
void emitNestedLoopTail(unsigned rank);
|
||||
void emitInfoAndNewLine(Operation *op);
|
||||
|
||||
/// MLIR component emitters.
|
||||
void emitOperation(Operation *op);
|
||||
|
@ -527,16 +528,16 @@ void ModuleEmitter::emitScfFor(scf::ForOp *op) {
|
|||
emitValue(iterVar);
|
||||
os << " += ";
|
||||
emitValue(op->step());
|
||||
os << ") {\n";
|
||||
os << ") {";
|
||||
emitInfoAndNewLine(*op);
|
||||
|
||||
addIndent();
|
||||
|
||||
if (auto pipeline = op->getAttrOfType<BoolAttr>("pipeline")) {
|
||||
indent();
|
||||
if (pipeline.getValue())
|
||||
if (pipeline.getValue()) {
|
||||
indent();
|
||||
os << "#pragma HLS pipeline\n";
|
||||
else
|
||||
os << "#pragma HLS pipeline off\n";
|
||||
}
|
||||
}
|
||||
|
||||
// if (auto flatten = op->getAttrOfType<BoolAttr>("flatten")) {
|
||||
|
@ -578,7 +579,9 @@ void ModuleEmitter::emitScfIf(scf::IfOp *op) {
|
|||
indent();
|
||||
os << "if (";
|
||||
emitValue(op->condition());
|
||||
os << ") {\n";
|
||||
os << ") {";
|
||||
emitInfoAndNewLine(*op);
|
||||
|
||||
addIndent();
|
||||
emitBlock(op->thenRegion().front());
|
||||
reduceIndent();
|
||||
|
@ -609,7 +612,8 @@ void ModuleEmitter::emitScfYield(scf::YieldOp *op) {
|
|||
emitValue(result, rank);
|
||||
os << " = ";
|
||||
emitValue(op->getOperand(resultIdx++), rank);
|
||||
os << ";\n";
|
||||
os << ";";
|
||||
emitInfoAndNewLine(*op);
|
||||
emitNestedLoopTail(rank);
|
||||
}
|
||||
}
|
||||
|
@ -663,16 +667,16 @@ void ModuleEmitter::emitAffineFor(AffineForOp *op) {
|
|||
|
||||
// Emit increase step.
|
||||
emitValue(iterVar);
|
||||
os << " += " << op->getStep() << ") {\n";
|
||||
os << " += " << op->getStep() << ") {";
|
||||
emitInfoAndNewLine(*op);
|
||||
|
||||
addIndent();
|
||||
|
||||
if (auto pipeline = op->getAttrOfType<BoolAttr>("pipeline")) {
|
||||
indent();
|
||||
if (pipeline.getValue())
|
||||
if (pipeline.getValue()) {
|
||||
indent();
|
||||
os << "#pragma HLS pipeline\n";
|
||||
else
|
||||
os << "#pragma HLS pipeline off\n";
|
||||
}
|
||||
}
|
||||
|
||||
// if (auto flatten = op->getAttrOfType<BoolAttr>("flatten")) {
|
||||
|
@ -729,7 +733,9 @@ void ModuleEmitter::emitAffineIf(AffineIfOp *op) {
|
|||
if (constrIdx++ != constrSet.getNumConstraints() - 1)
|
||||
os << " && ";
|
||||
}
|
||||
os << ") {\n";
|
||||
os << ") {";
|
||||
emitInfoAndNewLine(*op);
|
||||
|
||||
addIndent();
|
||||
emitBlock(*op->getThenBlock());
|
||||
reduceIndent();
|
||||
|
@ -799,8 +805,12 @@ void ModuleEmitter::emitAffineParallel(AffineParallelOp *op) {
|
|||
reduceIndent();
|
||||
|
||||
indent();
|
||||
os << "}\n";
|
||||
if (i == e - 1)
|
||||
os << "}";
|
||||
else
|
||||
os << "}\n";
|
||||
}
|
||||
emitInfoAndNewLine(*op);
|
||||
}
|
||||
|
||||
void ModuleEmitter::emitAffineApply(AffineApplyOp *op) {
|
||||
|
@ -810,7 +820,8 @@ void ModuleEmitter::emitAffineApply(AffineApplyOp *op) {
|
|||
auto affineMap = op->getAffineMap();
|
||||
AffineExprEmitter(state, affineMap.getNumDims(), op->getOperands())
|
||||
.emitAffineExpr(affineMap.getResult(0));
|
||||
os << ";\n";
|
||||
os << ";";
|
||||
emitInfoAndNewLine(*op);
|
||||
}
|
||||
|
||||
template <typename OpType>
|
||||
|
@ -829,7 +840,8 @@ void ModuleEmitter::emitAffineMaxMin(OpType *op, const char *syntax) {
|
|||
affineEmitter.emitAffineExpr(expr);
|
||||
os << ")";
|
||||
}
|
||||
os << ";\n";
|
||||
os << ";";
|
||||
emitInfoAndNewLine(*op);
|
||||
}
|
||||
|
||||
void ModuleEmitter::emitAffineLoad(AffineLoadOp *op) {
|
||||
|
@ -845,7 +857,8 @@ void ModuleEmitter::emitAffineLoad(AffineLoadOp *op) {
|
|||
affineEmitter.emitAffineExpr(index);
|
||||
os << "]";
|
||||
}
|
||||
os << ";\n";
|
||||
os << ";";
|
||||
emitInfoAndNewLine(*op);
|
||||
}
|
||||
|
||||
void ModuleEmitter::emitAffineStore(AffineStoreOp *op) {
|
||||
|
@ -861,7 +874,8 @@ void ModuleEmitter::emitAffineStore(AffineStoreOp *op) {
|
|||
}
|
||||
os << " = ";
|
||||
emitValue(op->getValueToStore());
|
||||
os << ";\n";
|
||||
os << ";";
|
||||
emitInfoAndNewLine(*op);
|
||||
}
|
||||
|
||||
void ModuleEmitter::emitAffineVectorLoad(AffineVectorLoadOp *op) {
|
||||
|
@ -890,7 +904,8 @@ void ModuleEmitter::emitAffineYield(AffineYieldOp *op) {
|
|||
emitValue(result, rank);
|
||||
os << " = ";
|
||||
emitValue(op->getOperand(resultIdx++), rank);
|
||||
os << ";\n";
|
||||
os << ";";
|
||||
emitInfoAndNewLine(*op);
|
||||
emitNestedLoopTail(rank);
|
||||
}
|
||||
} else if (auto parentOp =
|
||||
|
@ -916,7 +931,8 @@ void ModuleEmitter::emitAffineYield(AffineYieldOp *op) {
|
|||
emitValue(result, rank);
|
||||
os << " = ";
|
||||
emitValue(op->getOperand(resultIdx++), rank);
|
||||
os << ";\n";
|
||||
os << ";";
|
||||
emitInfoAndNewLine(*op);
|
||||
emitNestedLoopTail(rank);
|
||||
}
|
||||
reduceIndent();
|
||||
|
@ -970,7 +986,8 @@ void ModuleEmitter::emitAffineYield(AffineYieldOp *op) {
|
|||
emitValue(op->getOperand(resultIdx++), rank);
|
||||
break;
|
||||
}
|
||||
os << ";\n";
|
||||
os << ";";
|
||||
emitInfoAndNewLine(*op);
|
||||
emitNestedLoopTail(rank);
|
||||
}
|
||||
reduceIndent();
|
||||
|
@ -993,7 +1010,8 @@ template <typename OpType> void ModuleEmitter::emitAlloc(OpType *op) {
|
|||
|
||||
indent();
|
||||
emitArrayDecl(op->getResult());
|
||||
os << ";\n";
|
||||
os << ";";
|
||||
emitInfoAndNewLine(*op);
|
||||
}
|
||||
|
||||
void ModuleEmitter::emitLoad(LoadOp *op) {
|
||||
|
@ -1006,7 +1024,8 @@ void ModuleEmitter::emitLoad(LoadOp *op) {
|
|||
emitValue(index);
|
||||
os << "]";
|
||||
}
|
||||
os << ";\n";
|
||||
os << ";";
|
||||
emitInfoAndNewLine(*op);
|
||||
}
|
||||
|
||||
void ModuleEmitter::emitStore(StoreOp *op) {
|
||||
|
@ -1019,7 +1038,8 @@ void ModuleEmitter::emitStore(StoreOp *op) {
|
|||
}
|
||||
os << " = ";
|
||||
emitValue(op->getValueToStore());
|
||||
os << ";\n";
|
||||
os << ";";
|
||||
emitInfoAndNewLine(*op);
|
||||
}
|
||||
|
||||
/// Tensor-related statement emitters.
|
||||
|
@ -1029,7 +1049,8 @@ void ModuleEmitter::emitTensorLoad(TensorLoadOp *op) {
|
|||
emitValue(op->getResult(), rank);
|
||||
os << " = ";
|
||||
emitValue(op->getOperand(), rank);
|
||||
os << ";\n";
|
||||
os << ";";
|
||||
emitInfoAndNewLine(*op);
|
||||
emitNestedLoopTail(rank);
|
||||
}
|
||||
|
||||
|
@ -1039,7 +1060,8 @@ void ModuleEmitter::emitTensorStore(TensorStoreOp *op) {
|
|||
emitValue(op->getOperand(1), rank);
|
||||
os << " = ";
|
||||
emitValue(op->getOperand(0), rank);
|
||||
os << ";\n";
|
||||
os << ";";
|
||||
emitInfoAndNewLine(*op);
|
||||
emitNestedLoopTail(rank);
|
||||
}
|
||||
|
||||
|
@ -1065,7 +1087,8 @@ void ModuleEmitter::emitDim(DimOp *op) {
|
|||
indent();
|
||||
emitValue(op->getResult());
|
||||
os << " = ";
|
||||
os << type.getShape()[constVal] << ";\n";
|
||||
os << type.getShape()[constVal] << ";";
|
||||
emitInfoAndNewLine(*op);
|
||||
} else
|
||||
emitError(*op, "index is out of range.");
|
||||
} else
|
||||
|
@ -1080,7 +1103,8 @@ void ModuleEmitter::emitRank(RankOp *op) {
|
|||
indent();
|
||||
emitValue(op->getResult());
|
||||
os << " = ";
|
||||
os << type.getRank() << ";\n";
|
||||
os << type.getRank() << ";";
|
||||
emitInfoAndNewLine(*op);
|
||||
} else
|
||||
emitError(*op, "is unranked.");
|
||||
}
|
||||
|
@ -1094,7 +1118,8 @@ void ModuleEmitter::emitBinary(Operation *op, const char *syntax) {
|
|||
emitValue(op->getOperand(0), rank);
|
||||
os << " " << syntax << " ";
|
||||
emitValue(op->getOperand(1), rank);
|
||||
os << ";\n";
|
||||
os << ";";
|
||||
emitInfoAndNewLine(op);
|
||||
emitNestedLoopTail(rank);
|
||||
}
|
||||
|
||||
|
@ -1104,7 +1129,8 @@ void ModuleEmitter::emitUnary(Operation *op, const char *syntax) {
|
|||
emitValue(op->getResult(0), rank);
|
||||
os << " = " << syntax << "(";
|
||||
emitValue(op->getOperand(0), rank);
|
||||
os << ");\n";
|
||||
os << ");";
|
||||
emitInfoAndNewLine(op);
|
||||
emitNestedLoopTail(rank);
|
||||
}
|
||||
|
||||
|
@ -1123,7 +1149,8 @@ void ModuleEmitter::emitSelect(SelectOp *op) {
|
|||
emitValue(op->getTrueValue(), rank);
|
||||
os << " : ";
|
||||
emitValue(op->getFalseValue(), rank);
|
||||
os << ";\n";
|
||||
os << ";";
|
||||
emitInfoAndNewLine(*op);
|
||||
emitNestedLoopTail(rank);
|
||||
}
|
||||
|
||||
|
@ -1154,7 +1181,8 @@ void ModuleEmitter::emitConstant(ConstantOp *op) {
|
|||
if (elementIdx++ != denseAttr.getNumElements() - 1)
|
||||
os << ", ";
|
||||
}
|
||||
os << "};\n";
|
||||
os << "};";
|
||||
emitInfoAndNewLine(*op);
|
||||
} else
|
||||
emitError(*op, "has unsupported constant type.");
|
||||
}
|
||||
|
@ -1164,7 +1192,8 @@ void ModuleEmitter::emitIndexCast(IndexCastOp *op) {
|
|||
emitValue(op->getResult());
|
||||
os << " = ";
|
||||
emitValue(op->getOperand());
|
||||
os << ";\n";
|
||||
os << ";";
|
||||
emitInfoAndNewLine(*op);
|
||||
}
|
||||
|
||||
void ModuleEmitter::emitCall(CallOp *op) {
|
||||
|
@ -1204,7 +1233,8 @@ void ModuleEmitter::emitCall(CallOp *op) {
|
|||
emitValue(result);
|
||||
}
|
||||
|
||||
os << ");\n";
|
||||
os << ");";
|
||||
emitInfoAndNewLine(*op);
|
||||
}
|
||||
|
||||
/// Structure operation emitters.
|
||||
|
@ -1214,17 +1244,17 @@ void ModuleEmitter::emitAssign(AssignOp *op) {
|
|||
emitValue(op->getResult(), rank);
|
||||
os << " = ";
|
||||
emitValue(op->getOperand(), rank);
|
||||
os << ";\n";
|
||||
os << ";";
|
||||
emitInfoAndNewLine(*op);
|
||||
emitNestedLoopTail(rank);
|
||||
}
|
||||
|
||||
void ModuleEmitter::emitArray(ArrayOp *op) {
|
||||
bool emitPragmaFlag = false;
|
||||
|
||||
// Emit interface pragma.
|
||||
if (op->interface()) {
|
||||
emitPragmaFlag = true;
|
||||
|
||||
// Emit interface pragma.
|
||||
indent();
|
||||
os << "#pragma HLS interface";
|
||||
os << " " << op->interface_mode();
|
||||
|
@ -1233,21 +1263,21 @@ void ModuleEmitter::emitArray(ArrayOp *op) {
|
|||
if (op->interface_mode() == "m_axi") {
|
||||
os << " depth=" << op->interface_depth();
|
||||
os << " offset=slave";
|
||||
} else if (op->storage())
|
||||
os << " storage_type=" << op->storage_type();
|
||||
|
||||
}
|
||||
os << "\n";
|
||||
}
|
||||
|
||||
// Emit resource pragma.
|
||||
if (op->storage()) {
|
||||
emitPragmaFlag = true;
|
||||
indent();
|
||||
os << "#pragma HLS resource";
|
||||
os << " variable=";
|
||||
emitValue(op->getOperand());
|
||||
os << " core=";
|
||||
os << op->storage_type();
|
||||
os << "\n";
|
||||
}
|
||||
// This branch is prepared for 2020.1 or higher version.
|
||||
// else {
|
||||
// Emit bind_storage pragma.
|
||||
// indent();
|
||||
// os << "#pragma HLS bind_storage";
|
||||
// os << " variable=";
|
||||
// emitValue(op->getOperand());
|
||||
// os << " type=" << op->storage_type();
|
||||
// os << " impl=" << op->storage_impl() << "\n";
|
||||
//}
|
||||
|
||||
auto type = op->getOperand().getType().cast<ShapedType>();
|
||||
if (op->partition() && type.hasStaticShape()) {
|
||||
|
@ -1370,6 +1400,17 @@ void ModuleEmitter::emitNestedLoopTail(unsigned rank) {
|
|||
}
|
||||
}
|
||||
|
||||
void ModuleEmitter::emitInfoAndNewLine(Operation *op) {
|
||||
os << "\t//";
|
||||
if (auto loc = op->getLoc().dyn_cast<FileLineColLoc>())
|
||||
os << " #L" << loc.getLine();
|
||||
if (auto begin = op->getAttrOfType<IntegerAttr>("schedule_begin"))
|
||||
os << ", [" << begin.getUInt();
|
||||
if (auto end = op->getAttrOfType<IntegerAttr>("schedule_end"))
|
||||
os << ", " << end.getUInt() << ")";
|
||||
os << "\n";
|
||||
}
|
||||
|
||||
/// MLIR component emitters.
|
||||
void ModuleEmitter::emitOperation(Operation *op) {
|
||||
if (ExprVisitor(*this).dispatchVisitor(op))
|
||||
|
@ -1393,6 +1434,12 @@ void ModuleEmitter::emitFunction(FuncOp func) {
|
|||
if (func.getBlocks().size() != 1)
|
||||
emitError(func, "has zero or more than one basic blocks.");
|
||||
|
||||
if (auto top = func.getAttrOfType<BoolAttr>("top_function"))
|
||||
os << "/// This is top function.\n";
|
||||
|
||||
if (auto latency = func.getAttrOfType<IntegerAttr>("latency"))
|
||||
os << "/// Function latency is " << latency.getUInt() << ".\n";
|
||||
|
||||
// Emit function signature.
|
||||
os << "void " << func.getName() << "(\n";
|
||||
addIndent();
|
||||
|
@ -1434,7 +1481,8 @@ void ModuleEmitter::emitFunction(FuncOp func) {
|
|||
emitError(func, "doesn't have a return operation as terminator.");
|
||||
|
||||
reduceIndent();
|
||||
os << "\n) {\n";
|
||||
os << "\n) {";
|
||||
emitInfoAndNewLine(func);
|
||||
|
||||
// Emit function body.
|
||||
addIndent();
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
// CHECK-LABEL: func @test_conversion(
|
||||
// CHECK-SAME: %arg0: f32, %arg1: memref<16xf32>) -> (f32, memref<16xf32>, i32, tensor<2x2xi32>) attributes {dataflow = false, top_function = true} {
|
||||
func @test_conversion(%arg0: f32, %arg1: memref<16xf32>) -> (f32, memref<16xf32>, i32, tensor<2x2xi32>) {
|
||||
// CHECK: %[[VAL_0:.*]] = "hlscpp.array"(%[[ARG_1:.*]]) {interface = true, partition = false, storage = false} : (memref<16xf32>) -> memref<16xf32>
|
||||
// CHECK: %[[VAL_0:.*]] = "hlscpp.array"(%[[ARG_1:.*]]) {interface = true, partition = false, storage = true, storage_type = "ram_1p_bram"} : (memref<16xf32>) -> memref<16xf32>
|
||||
%c11_i32 = constant 11 : i32
|
||||
%cst = constant dense<[[11, 0], [0, -42]]> : tensor<2x2xi32>
|
||||
|
||||
// CHECK: %[[VAL_1:.*]] = "hlscpp.array"(%cst) {interface = false, partition = false, storage = false} : (tensor<2x2xi32>) -> tensor<2x2xi32>
|
||||
// CHECK: %[[VAL_1:.*]] = "hlscpp.array"(%cst) {interface = false, partition = false, storage = true, storage_type = "ram_1p_bram"} : (tensor<2x2xi32>) -> tensor<2x2xi32>
|
||||
// CHECK: %[[VAL_2:.*]] = "hlscpp.assign"(%arg0) : (f32) -> f32
|
||||
// CHECK: %[[VAL_3:.*]] = "hlscpp.assign"(%[[VAL_0:.*]]) : (memref<16xf32>) -> memref<16xf32>
|
||||
// CHECK: %[[VAL_4:.*]] = "hlscpp.assign"(%c11_i32) : (i32) -> i32
|
||||
|
|
Loading…
Reference in New Issue