[EmitHLSCpp] shaped-type related operation emitters are further optimized; AffineYieldOp supports shaped-type; update readme and TODOs
This commit is contained in:
parent
1df6af6af6
commit
417b10f68d
|
@ -13,12 +13,11 @@ After the installation and test successfully completed, you should be able to ru
|
||||||
```sh
|
```sh
|
||||||
bin/hlsld-translate -emit-hlscpp ../test/EmitHLSCpp/test_*.mlir
|
bin/hlsld-translate -emit-hlscpp ../test/EmitHLSCpp/test_*.mlir
|
||||||
```
|
```
|
||||||
|
|
||||||
## TODO List
|
## TODO List
|
||||||
### hlsld-translate: EmitHLSCpp
|
### hlsld-translate: EmitHLSCpp
|
||||||
1. **Test HLS C++ emitter with some real benchmarks;**
|
1. **Test HLS C++ emitter with some real benchmarks;**
|
||||||
2. Support extract_element/tensor_from_elements/splat operations;
|
2. **TODOs in EmitHLSCpp.cpp;**
|
||||||
3. Support call operation;
|
3. **Create testcase for each important operation;**
|
||||||
4. Support memref/tensor cast/view/subview operations;
|
4. Support memref/tensor cast/view/subview operations;
|
||||||
5. Support atomic/complex/extend -related operations.
|
5. Support atomic/complex/extend -related operations.
|
||||||
|
|
||||||
|
|
|
@ -91,6 +91,7 @@ private:
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
// TODO: update naming rule.
|
||||||
SmallString<8> HLSCppEmitterBase::addName(Value val, bool isPtr) {
|
SmallString<8> HLSCppEmitterBase::addName(Value val, bool isPtr) {
|
||||||
assert(!isDeclared(val) && "has been declared before.");
|
assert(!isDeclared(val) && "has been declared before.");
|
||||||
|
|
||||||
|
@ -154,8 +155,8 @@ public:
|
||||||
// Complex expressions.
|
// Complex expressions.
|
||||||
AddCFOp, SubCFOp, ImOp, ReOp, CreateComplexOp,
|
AddCFOp, SubCFOp, ImOp, ReOp, CreateComplexOp,
|
||||||
// Special operations.
|
// Special operations.
|
||||||
CopySignOp, TruncateIOp, ZeroExtendIOp, SignExtendIOp, IndexCastOp,
|
SelectOp, ConstantOp, CopySignOp, TruncateIOp, ZeroExtendIOp,
|
||||||
SelectOp, ConstantOp, CallOp, ReturnOp>(
|
SignExtendIOp, IndexCastOp, CallOp, ReturnOp>(
|
||||||
[&](auto opNode) -> ResultType {
|
[&](auto opNode) -> ResultType {
|
||||||
return thisCast->visitOp(opNode, args...);
|
return thisCast->visitOp(opNode, args...);
|
||||||
})
|
})
|
||||||
|
@ -268,13 +269,13 @@ public:
|
||||||
HANDLE(CreateComplexOp);
|
HANDLE(CreateComplexOp);
|
||||||
|
|
||||||
// Special operations.
|
// Special operations.
|
||||||
|
HANDLE(SelectOp);
|
||||||
|
HANDLE(ConstantOp);
|
||||||
HANDLE(CopySignOp);
|
HANDLE(CopySignOp);
|
||||||
HANDLE(TruncateIOp);
|
HANDLE(TruncateIOp);
|
||||||
HANDLE(ZeroExtendIOp);
|
HANDLE(ZeroExtendIOp);
|
||||||
HANDLE(SignExtendIOp);
|
HANDLE(SignExtendIOp);
|
||||||
HANDLE(IndexCastOp);
|
HANDLE(IndexCastOp);
|
||||||
HANDLE(SelectOp);
|
|
||||||
HANDLE(ConstantOp);
|
|
||||||
HANDLE(CallOp);
|
HANDLE(CallOp);
|
||||||
HANDLE(ReturnOp);
|
HANDLE(ReturnOp);
|
||||||
#undef HANDLE
|
#undef HANDLE
|
||||||
|
@ -329,23 +330,22 @@ public:
|
||||||
void emitUnary(Operation *op, const char *syntax);
|
void emitUnary(Operation *op, const char *syntax);
|
||||||
|
|
||||||
/// Special operation emitters.
|
/// Special operation emitters.
|
||||||
void emitIndexCast(IndexCastOp *op);
|
|
||||||
void emitSelect(SelectOp *op);
|
void emitSelect(SelectOp *op);
|
||||||
void emitConstant(ConstantOp *op);
|
void emitConstant(ConstantOp *op);
|
||||||
|
void emitIndexCast(IndexCastOp *op);
|
||||||
void emitCall(CallOp *op);
|
void emitCall(CallOp *op);
|
||||||
|
|
||||||
/// Top-level MLIR module emitter.
|
/// Top-level MLIR module emitter.
|
||||||
void emitModule(ModuleOp module);
|
void emitModule(ModuleOp module);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// C++ structure emitters.
|
/// C++ component emitters.
|
||||||
|
void emitValue(Value val, unsigned rank = 0, bool isPtr = false);
|
||||||
void emitArrayDecl(Value array);
|
void emitArrayDecl(Value array);
|
||||||
void emitNestedLoopHead(ShapedType type);
|
unsigned emitNestedLoopHead(Value val);
|
||||||
void emitNestedLoopTail(ShapedType type);
|
void emitNestedLoopTail(unsigned rank);
|
||||||
void emitNestedLoopArray(ShapedType type, Value val);
|
|
||||||
|
|
||||||
/// MLIR component emitters.
|
/// MLIR component emitters.
|
||||||
void emitValue(Value val, bool isPtr = false, bool isRef = false);
|
|
||||||
void emitOperation(Operation *op);
|
void emitOperation(Operation *op);
|
||||||
void emitBlock(Block &block);
|
void emitBlock(Block &block);
|
||||||
void emitFunction(FuncOp func);
|
void emitFunction(FuncOp func);
|
||||||
|
@ -522,9 +522,9 @@ public:
|
||||||
bool visitOp(Log10Op op) { return emitter.emitUnary(op, "log10"), true; }
|
bool visitOp(Log10Op op) { return emitter.emitUnary(op, "log10"), true; }
|
||||||
|
|
||||||
/// Special operations.
|
/// Special operations.
|
||||||
bool visitOp(IndexCastOp op) { return emitter.emitIndexCast(&op), true; }
|
|
||||||
bool visitOp(SelectOp op) { return emitter.emitSelect(&op), true; }
|
bool visitOp(SelectOp op) { return emitter.emitSelect(&op), true; }
|
||||||
bool visitOp(ConstantOp op) { return emitter.emitConstant(&op), true; }
|
bool visitOp(ConstantOp op) { return emitter.emitConstant(&op), true; }
|
||||||
|
bool visitOp(IndexCastOp op) { return emitter.emitIndexCast(&op), true; }
|
||||||
bool visitOp(CallOp op) { return emitter.emitCall(&op), true; }
|
bool visitOp(CallOp op) { return emitter.emitCall(&op), true; }
|
||||||
bool visitOp(ReturnOp op) { return true; }
|
bool visitOp(ReturnOp op) { return true; }
|
||||||
|
|
||||||
|
@ -641,14 +641,18 @@ void ModuleEmitter::emitAffineFor(AffineForOp *op) {
|
||||||
os << "}\n";
|
os << "}\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO: support to yield shaped type.
|
|
||||||
void ModuleEmitter::emitAffineIf(AffineIfOp *op) {
|
void ModuleEmitter::emitAffineIf(AffineIfOp *op) {
|
||||||
// Declare all values returned by AffineYieldOp. They will be further handled
|
// Declare all values returned by AffineYieldOp. They will be further handled
|
||||||
// by the AffineYieldOp emitter.
|
// by the AffineYieldOp emitter.
|
||||||
for (auto result : op->getResults()) {
|
for (auto result : op->getResults()) {
|
||||||
indent();
|
if (!isDeclared(result)) {
|
||||||
emitValue(result);
|
indent();
|
||||||
os << ";\n";
|
if (result.getType().isa<ShapedType>())
|
||||||
|
emitArrayDecl(result);
|
||||||
|
else
|
||||||
|
emitValue(result);
|
||||||
|
os << ";\n";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
indent();
|
indent();
|
||||||
|
@ -686,14 +690,18 @@ void ModuleEmitter::emitAffineIf(AffineIfOp *op) {
|
||||||
os << "}\n";
|
os << "}\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO: support to yield shaped type.
|
|
||||||
void ModuleEmitter::emitAffineParallel(AffineParallelOp *op) {
|
void ModuleEmitter::emitAffineParallel(AffineParallelOp *op) {
|
||||||
// Declare all values returned by AffineParallelOp. They will be further
|
// Declare all values returned by AffineParallelOp. They will be further
|
||||||
// handled by the AffineYieldOp emitter.
|
// handled by the AffineYieldOp emitter.
|
||||||
for (auto result : op->getResults()) {
|
for (auto result : op->getResults()) {
|
||||||
indent();
|
if (!isDeclared(result)) {
|
||||||
emitValue(result);
|
indent();
|
||||||
os << ";\n";
|
if (result.getType().isa<ShapedType>())
|
||||||
|
emitArrayDecl(result);
|
||||||
|
else
|
||||||
|
emitValue(result);
|
||||||
|
os << ";\n";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unsigned i = 0, e = op->getNumDims(); i < e; ++i) {
|
for (unsigned i = 0, e = op->getNumDims(); i < e; ++i) {
|
||||||
|
@ -808,7 +816,10 @@ void ModuleEmitter::emitAffineVectorStore(AffineVectorStoreOp *op) {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO: support to yield shaped type.
|
// TODO: For now, all values created in the affine if/parallel region will be
|
||||||
|
// declared in the generated C++. However, values which will be returned by
|
||||||
|
// affine yield operation should not be declared again. How to "bind" the pair
|
||||||
|
// of values inside/outside of affine if/parallel region needs to be considered.
|
||||||
void ModuleEmitter::emitAffineYield(AffineYieldOp *op) {
|
void ModuleEmitter::emitAffineYield(AffineYieldOp *op) {
|
||||||
if (op->getNumOperands() == 0)
|
if (op->getNumOperands() == 0)
|
||||||
return;
|
return;
|
||||||
|
@ -818,12 +829,13 @@ void ModuleEmitter::emitAffineYield(AffineYieldOp *op) {
|
||||||
if (auto parentOp = dyn_cast<AffineIfOp>(op->getParentOp())) {
|
if (auto parentOp = dyn_cast<AffineIfOp>(op->getParentOp())) {
|
||||||
unsigned resultIdx = 0;
|
unsigned resultIdx = 0;
|
||||||
for (auto result : parentOp.getResults()) {
|
for (auto result : parentOp.getResults()) {
|
||||||
|
unsigned rank = emitNestedLoopHead(result);
|
||||||
indent();
|
indent();
|
||||||
emitValue(result);
|
emitValue(result, rank);
|
||||||
os << " = ";
|
os << " = ";
|
||||||
emitValue(op->getOperand(resultIdx));
|
emitValue(op->getOperand(resultIdx++), rank);
|
||||||
os << ";\n";
|
os << ";\n";
|
||||||
resultIdx += 1;
|
emitNestedLoopTail(rank);
|
||||||
}
|
}
|
||||||
} else if (auto parentOp =
|
} else if (auto parentOp =
|
||||||
dyn_cast<mlir::AffineParallelOp>(op->getParentOp())) {
|
dyn_cast<mlir::AffineParallelOp>(op->getParentOp())) {
|
||||||
|
@ -843,12 +855,13 @@ void ModuleEmitter::emitAffineYield(AffineYieldOp *op) {
|
||||||
addIndent();
|
addIndent();
|
||||||
unsigned resultIdx = 0;
|
unsigned resultIdx = 0;
|
||||||
for (auto result : parentOp.getResults()) {
|
for (auto result : parentOp.getResults()) {
|
||||||
|
unsigned rank = emitNestedLoopHead(result);
|
||||||
indent();
|
indent();
|
||||||
emitValue(result);
|
emitValue(result, rank);
|
||||||
os << " = ";
|
os << " = ";
|
||||||
emitValue(op->getOperand(resultIdx));
|
emitValue(op->getOperand(resultIdx++), rank);
|
||||||
os << ";\n";
|
os << ";\n";
|
||||||
resultIdx += 1;
|
emitNestedLoopTail(rank);
|
||||||
}
|
}
|
||||||
reduceIndent();
|
reduceIndent();
|
||||||
|
|
||||||
|
@ -860,8 +873,9 @@ void ModuleEmitter::emitAffineYield(AffineYieldOp *op) {
|
||||||
addIndent();
|
addIndent();
|
||||||
resultIdx = 0;
|
resultIdx = 0;
|
||||||
for (auto result : parentOp.getResults()) {
|
for (auto result : parentOp.getResults()) {
|
||||||
|
unsigned rank = emitNestedLoopHead(result);
|
||||||
indent();
|
indent();
|
||||||
emitValue(result);
|
emitValue(result, rank);
|
||||||
switch ((AtomicRMWKind)parentOp
|
switch ((AtomicRMWKind)parentOp
|
||||||
.getAttrOfType<ArrayAttr>(
|
.getAttrOfType<ArrayAttr>(
|
||||||
parentOp.getReductionsAttrName())[resultIdx]
|
parentOp.getReductionsAttrName())[resultIdx]
|
||||||
|
@ -870,39 +884,38 @@ void ModuleEmitter::emitAffineYield(AffineYieldOp *op) {
|
||||||
case (AtomicRMWKind::addf):
|
case (AtomicRMWKind::addf):
|
||||||
case (AtomicRMWKind::addi):
|
case (AtomicRMWKind::addi):
|
||||||
os << " += ";
|
os << " += ";
|
||||||
emitValue(op->getOperand(resultIdx));
|
emitValue(op->getOperand(resultIdx++), rank);
|
||||||
break;
|
break;
|
||||||
case (AtomicRMWKind::assign):
|
case (AtomicRMWKind::assign):
|
||||||
os << " = ";
|
os << " = ";
|
||||||
emitValue(op->getOperand(resultIdx));
|
emitValue(op->getOperand(resultIdx++), rank);
|
||||||
break;
|
break;
|
||||||
case (AtomicRMWKind::maxf):
|
case (AtomicRMWKind::maxf):
|
||||||
case (AtomicRMWKind::maxs):
|
case (AtomicRMWKind::maxs):
|
||||||
case (AtomicRMWKind::maxu):
|
case (AtomicRMWKind::maxu):
|
||||||
os << " = max(";
|
os << " = max(";
|
||||||
emitValue(result);
|
emitValue(result, rank);
|
||||||
os << ", ";
|
os << ", ";
|
||||||
emitValue(op->getOperand(resultIdx));
|
emitValue(op->getOperand(resultIdx++), rank);
|
||||||
os << ")";
|
os << ")";
|
||||||
break;
|
break;
|
||||||
case (AtomicRMWKind::minf):
|
case (AtomicRMWKind::minf):
|
||||||
case (AtomicRMWKind::mins):
|
case (AtomicRMWKind::mins):
|
||||||
case (AtomicRMWKind::minu):
|
case (AtomicRMWKind::minu):
|
||||||
os << " = min(";
|
os << " = min(";
|
||||||
emitValue(result);
|
emitValue(result, rank);
|
||||||
os << ", ";
|
os << ", ";
|
||||||
emitValue(op->getOperand(resultIdx));
|
emitValue(op->getOperand(resultIdx++), rank);
|
||||||
os << ")";
|
os << ")";
|
||||||
break;
|
break;
|
||||||
case (AtomicRMWKind::mulf):
|
case (AtomicRMWKind::mulf):
|
||||||
case (AtomicRMWKind::muli):
|
case (AtomicRMWKind::muli):
|
||||||
os << " *= ";
|
os << " *= ";
|
||||||
emitValue(op->getOperand(resultIdx));
|
emitValue(op->getOperand(resultIdx++), rank);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
os << ";\n";
|
os << ";\n";
|
||||||
resultIdx += 1;
|
emitNestedLoopTail(rank);
|
||||||
}
|
}
|
||||||
reduceIndent();
|
reduceIndent();
|
||||||
|
|
||||||
|
@ -956,46 +969,23 @@ void ModuleEmitter::emitStore(StoreOp *op) {
|
||||||
|
|
||||||
/// Tensor-related statement emitters.
|
/// Tensor-related statement emitters.
|
||||||
void ModuleEmitter::emitTensorLoad(TensorLoadOp *op) {
|
void ModuleEmitter::emitTensorLoad(TensorLoadOp *op) {
|
||||||
auto type = op->getType();
|
auto rank = emitNestedLoopHead(op->getResult());
|
||||||
if (!type.hasStaticShape()) {
|
|
||||||
emitError(*op, "is unranked or has dynamic shape.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Declare a new tensor.
|
|
||||||
indent();
|
indent();
|
||||||
emitArrayDecl(op->getResult());
|
emitValue(op->getResult(), rank);
|
||||||
os << ";\n";
|
|
||||||
|
|
||||||
// Create a nested loop for loading tensor.
|
|
||||||
emitNestedLoopHead(type);
|
|
||||||
|
|
||||||
indent();
|
|
||||||
emitNestedLoopArray(type, op->getResult());
|
|
||||||
os << " = ";
|
os << " = ";
|
||||||
emitNestedLoopArray(type, op->getOperand());
|
emitValue(op->getOperand(), rank);
|
||||||
os << ";\n";
|
os << ";\n";
|
||||||
|
emitNestedLoopTail(rank);
|
||||||
emitNestedLoopTail(type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModuleEmitter::emitTensorStore(TensorStoreOp *op) {
|
void ModuleEmitter::emitTensorStore(TensorStoreOp *op) {
|
||||||
auto type = op->getOperand(0).getType().cast<TensorType>();
|
auto rank = emitNestedLoopHead(op->getOperand(0));
|
||||||
if (!type.hasStaticShape()) {
|
|
||||||
emitError(*op, "is unranked or has dynamic shape.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a nested loop for storing tensor.
|
|
||||||
emitNestedLoopHead(type);
|
|
||||||
|
|
||||||
indent();
|
indent();
|
||||||
emitNestedLoopArray(type, op->getOperand(1));
|
emitValue(op->getOperand(1), rank);
|
||||||
os << " = ";
|
os << " = ";
|
||||||
emitNestedLoopArray(type, op->getOperand(0));
|
emitValue(op->getOperand(0), rank);
|
||||||
os << ";\n";
|
os << ";\n";
|
||||||
|
emitNestedLoopTail(rank);
|
||||||
emitNestedLoopTail(type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModuleEmitter::emitSplat(SplatOp *op) {
|
void ModuleEmitter::emitSplat(SplatOp *op) {
|
||||||
|
@ -1042,126 +1032,54 @@ void ModuleEmitter::emitRank(RankOp *op) {
|
||||||
|
|
||||||
/// Standard expression emitters.
|
/// Standard expression emitters.
|
||||||
void ModuleEmitter::emitBinary(Operation *op, const char *syntax) {
|
void ModuleEmitter::emitBinary(Operation *op, const char *syntax) {
|
||||||
if (auto type = op->getResult(0).getType().dyn_cast<ShapedType>()) {
|
auto rank = emitNestedLoopHead(op->getResult(0));
|
||||||
if (!type.hasStaticShape()) {
|
indent();
|
||||||
emitError(op, "is unranked or has dynamic shape.");
|
emitValue(op->getResult(0), rank);
|
||||||
return;
|
os << " = ";
|
||||||
}
|
emitValue(op->getOperand(0), rank);
|
||||||
|
os << " " << syntax << " ";
|
||||||
// Declare a new tensor.
|
emitValue(op->getOperand(1), rank);
|
||||||
indent();
|
os << ";\n";
|
||||||
emitArrayDecl(op->getResult(0));
|
emitNestedLoopTail(rank);
|
||||||
os << ";\n";
|
|
||||||
|
|
||||||
// Create a nested loop for element-wise binary operation.
|
|
||||||
emitNestedLoopHead(type);
|
|
||||||
|
|
||||||
indent();
|
|
||||||
emitNestedLoopArray(type, op->getResult(0));
|
|
||||||
os << " = ";
|
|
||||||
emitNestedLoopArray(type, op->getOperand(0));
|
|
||||||
os << " " << syntax << " ";
|
|
||||||
emitNestedLoopArray(type, op->getOperand(1));
|
|
||||||
os << ";\n";
|
|
||||||
|
|
||||||
emitNestedLoopTail(type);
|
|
||||||
} else {
|
|
||||||
indent();
|
|
||||||
emitValue(op->getResult(0));
|
|
||||||
os << " = ";
|
|
||||||
emitValue(op->getOperand(0));
|
|
||||||
os << " " << syntax << " ";
|
|
||||||
emitValue(op->getOperand(1));
|
|
||||||
os << ";\n";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModuleEmitter::emitUnary(Operation *op, const char *syntax) {
|
void ModuleEmitter::emitUnary(Operation *op, const char *syntax) {
|
||||||
if (auto type = op->getResult(0).getType().dyn_cast<ShapedType>()) {
|
auto rank = emitNestedLoopHead(op->getResult(0));
|
||||||
if (!type.hasStaticShape()) {
|
indent();
|
||||||
emitError(op, "is unranked or has dynamic shape.");
|
emitValue(op->getResult(0), rank);
|
||||||
return;
|
os << " = " << syntax << "(";
|
||||||
}
|
emitValue(op->getOperand(0), rank);
|
||||||
|
os << ");\n";
|
||||||
// Declare a new tensor.
|
emitNestedLoopTail(rank);
|
||||||
indent();
|
|
||||||
emitArrayDecl(op->getResult(0));
|
|
||||||
os << ";\n";
|
|
||||||
|
|
||||||
// Create a nested loop for element-wise unary operation.
|
|
||||||
emitNestedLoopHead(type);
|
|
||||||
|
|
||||||
indent();
|
|
||||||
emitNestedLoopArray(type, op->getResult(0));
|
|
||||||
os << " = " << syntax << "(";
|
|
||||||
emitNestedLoopArray(type, op->getOperand(0));
|
|
||||||
os << ");\n";
|
|
||||||
|
|
||||||
emitNestedLoopTail(type);
|
|
||||||
} else {
|
|
||||||
indent();
|
|
||||||
emitValue(op->getResult(0));
|
|
||||||
os << " = " << syntax << "(";
|
|
||||||
emitValue(op->getOperand(0));
|
|
||||||
os << ");\n";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Special operation emitters.
|
/// Special operation emitters.
|
||||||
void ModuleEmitter::emitIndexCast(IndexCastOp *op) {
|
|
||||||
indent();
|
|
||||||
emitValue(op->getResult());
|
|
||||||
os << " = ";
|
|
||||||
emitValue(op->getOperand());
|
|
||||||
os << ";\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModuleEmitter::emitSelect(SelectOp *op) {
|
void ModuleEmitter::emitSelect(SelectOp *op) {
|
||||||
if (auto type = op->getResult().getType().dyn_cast<ShapedType>()) {
|
unsigned rank = emitNestedLoopHead(op->getResult());
|
||||||
if (!type.hasStaticShape()) {
|
unsigned conditionRank = rank;
|
||||||
emitError(*op, "is unranked or has dynamic shape.");
|
if (!op->getCondition().getType().isa<ShapedType>())
|
||||||
return;
|
conditionRank = 0;
|
||||||
}
|
|
||||||
|
|
||||||
// Declare a new tensor.
|
indent();
|
||||||
indent();
|
emitValue(op->getResult(), rank);
|
||||||
emitArrayDecl(op->getResult());
|
os << " = ";
|
||||||
os << ";\n";
|
emitValue(op->getCondition(), conditionRank);
|
||||||
|
os << " ? ";
|
||||||
// Create a nested loop for element-wise select operation.
|
emitValue(op->getTrueValue(), rank);
|
||||||
emitNestedLoopHead(type);
|
os << " : ";
|
||||||
|
emitValue(op->getFalseValue(), rank);
|
||||||
indent();
|
os << ";\n";
|
||||||
emitNestedLoopArray(type, op->getResult());
|
emitNestedLoopTail(rank);
|
||||||
os << " = ";
|
|
||||||
if (auto condType = op->getCondition().getType().dyn_cast<ShapedType>()) {
|
|
||||||
emitNestedLoopArray(condType, op->getCondition());
|
|
||||||
} else {
|
|
||||||
emitValue(op->getCondition());
|
|
||||||
}
|
|
||||||
os << " ? ";
|
|
||||||
emitNestedLoopArray(type, op->getTrueValue());
|
|
||||||
os << " : ";
|
|
||||||
emitNestedLoopArray(type, op->getFalseValue());
|
|
||||||
os << ";\n";
|
|
||||||
|
|
||||||
emitNestedLoopTail(type);
|
|
||||||
} else {
|
|
||||||
indent();
|
|
||||||
emitValue(op->getResult());
|
|
||||||
os << " = ";
|
|
||||||
emitValue(op->getCondition());
|
|
||||||
os << " ? ";
|
|
||||||
emitValue(op->getTrueValue());
|
|
||||||
os << " : ";
|
|
||||||
emitValue(op->getFalseValue());
|
|
||||||
os << ";\n";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModuleEmitter::emitConstant(ConstantOp *op) {
|
void ModuleEmitter::emitConstant(ConstantOp *op) {
|
||||||
if (isDeclared(op->getResult()))
|
if (isDeclared(op->getResult())) {
|
||||||
|
// TODO: This case should be supported somehow.
|
||||||
|
if (op->getResult().getType().isa<ShapedType>())
|
||||||
|
emitError(*op, "constant array can't be directly returned for now.");
|
||||||
|
// This indicates the constant type is scalar (float, integer, or bool).
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (auto denseAttr = op->getValue().dyn_cast<DenseElementsAttr>()) {
|
if (auto denseAttr = op->getValue().dyn_cast<DenseElementsAttr>()) {
|
||||||
indent();
|
indent();
|
||||||
|
@ -1190,58 +1108,27 @@ void ModuleEmitter::emitConstant(ConstantOp *op) {
|
||||||
emitError(*op, "has unsupported constant type.");
|
emitError(*op, "has unsupported constant type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ModuleEmitter::emitIndexCast(IndexCastOp *op) {
|
||||||
|
indent();
|
||||||
|
emitValue(op->getResult());
|
||||||
|
os << " = ";
|
||||||
|
emitValue(op->getOperand());
|
||||||
|
os << ";\n";
|
||||||
|
}
|
||||||
|
|
||||||
void ModuleEmitter::emitCall(CallOp *op) {
|
void ModuleEmitter::emitCall(CallOp *op) {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
/// C++ structure emitters.
|
/// C++ component emitters.
|
||||||
void ModuleEmitter::emitArrayDecl(Value array) {
|
void ModuleEmitter::emitValue(Value val, unsigned rank, bool isPtr) {
|
||||||
assert(!isDeclared(array) && "has been declared before.");
|
assert(!(rank && isPtr) && "should be either an array or a pointer.");
|
||||||
|
|
||||||
auto arrayType = array.getType().cast<ShapedType>();
|
|
||||||
if (arrayType.hasStaticShape()) {
|
|
||||||
emitValue(array);
|
|
||||||
for (auto &shape : arrayType.getShape())
|
|
||||||
os << "[" << shape << "]";
|
|
||||||
} else
|
|
||||||
emitValue(array, /*isPtr=*/true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModuleEmitter::emitNestedLoopHead(ShapedType type) {
|
|
||||||
unsigned dimIdx = 0;
|
|
||||||
for (auto &shape : type.getShape()) {
|
|
||||||
indent();
|
|
||||||
os << "for (int idx" << dimIdx << " = 0; ";
|
|
||||||
os << "idx" << dimIdx << " < " << shape << "; ";
|
|
||||||
os << "++idx" << dimIdx << ") {\n";
|
|
||||||
|
|
||||||
addIndent();
|
|
||||||
dimIdx += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModuleEmitter::emitNestedLoopTail(ShapedType type) {
|
|
||||||
for (unsigned i = 0; i < type.getRank(); ++i) {
|
|
||||||
reduceIndent();
|
|
||||||
|
|
||||||
indent();
|
|
||||||
os << "}\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModuleEmitter::emitNestedLoopArray(ShapedType type, Value val) {
|
|
||||||
emitValue(val);
|
|
||||||
for (unsigned i = 0; i < type.getRank(); ++i)
|
|
||||||
os << "[idx" << i << "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// MLIR component emitters.
|
|
||||||
void ModuleEmitter::emitValue(Value val, bool isPtr, bool isRef) {
|
|
||||||
assert(!(isPtr && isRef) && "should be either pointer or reference.");
|
|
||||||
|
|
||||||
// Value has been declared before or is a constant number.
|
// Value has been declared before or is a constant number.
|
||||||
if (isDeclared(val)) {
|
if (isDeclared(val)) {
|
||||||
os << getName(val);
|
os << getName(val);
|
||||||
|
for (unsigned i = 0; i < rank; ++i)
|
||||||
|
os << "[idx" << i << "]";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1278,11 +1165,65 @@ void ModuleEmitter::emitValue(Value val, bool isPtr, bool isRef) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the new value to nameTable and emit its name.
|
// Add the new value to nameTable and emit its name.
|
||||||
if (isRef)
|
|
||||||
os << "&";
|
|
||||||
os << addName(val, isPtr);
|
os << addName(val, isPtr);
|
||||||
|
for (unsigned i = 0; i < rank; ++i)
|
||||||
|
os << "[idx" << i << "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ModuleEmitter::emitArrayDecl(Value array) {
|
||||||
|
assert(!isDeclared(array) && "has been declared before.");
|
||||||
|
|
||||||
|
auto arrayType = array.getType().cast<ShapedType>();
|
||||||
|
if (arrayType.hasStaticShape()) {
|
||||||
|
emitValue(array);
|
||||||
|
for (auto &shape : arrayType.getShape())
|
||||||
|
os << "[" << shape << "]";
|
||||||
|
} else
|
||||||
|
emitValue(array, /*rank=*/0, /*isPtr=*/true);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned ModuleEmitter::emitNestedLoopHead(Value val) {
|
||||||
|
unsigned rank = 0;
|
||||||
|
|
||||||
|
if (auto type = val.getType().dyn_cast<ShapedType>()) {
|
||||||
|
if (!type.hasStaticShape()) {
|
||||||
|
emitError(val.getDefiningOp(), "is unranked or has dynamic shape.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declare a new array.
|
||||||
|
if (!isDeclared(val)) {
|
||||||
|
indent();
|
||||||
|
emitArrayDecl(val);
|
||||||
|
os << ";\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create nested loop.
|
||||||
|
unsigned dimIdx = 0;
|
||||||
|
for (auto &shape : type.getShape()) {
|
||||||
|
indent();
|
||||||
|
os << "for (int idx" << dimIdx << " = 0; ";
|
||||||
|
os << "idx" << dimIdx << " < " << shape << "; ";
|
||||||
|
os << "++idx" << dimIdx++ << ") {\n";
|
||||||
|
|
||||||
|
addIndent();
|
||||||
|
}
|
||||||
|
rank = type.getRank();
|
||||||
|
}
|
||||||
|
|
||||||
|
return rank;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModuleEmitter::emitNestedLoopTail(unsigned rank) {
|
||||||
|
for (unsigned i = 0; i < rank; ++i) {
|
||||||
|
reduceIndent();
|
||||||
|
|
||||||
|
indent();
|
||||||
|
os << "}\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// MLIR component emitters.
|
||||||
void ModuleEmitter::emitOperation(Operation *op) {
|
void ModuleEmitter::emitOperation(Operation *op) {
|
||||||
if (ExprVisitor(*this).dispatchVisitor(op))
|
if (ExprVisitor(*this).dispatchVisitor(op))
|
||||||
return;
|
return;
|
||||||
|
@ -1327,9 +1268,8 @@ void ModuleEmitter::emitFunction(FuncOp func) {
|
||||||
if (result.getType().isa<ShapedType>())
|
if (result.getType().isa<ShapedType>())
|
||||||
emitArrayDecl(result);
|
emitArrayDecl(result);
|
||||||
else
|
else
|
||||||
// In Vivado HLS, reference indicates the value is an output.
|
// In Vivado HLS, pointer indicates the value is an output.
|
||||||
// emitValue(result, /*isPtr=*/false, /*isRef=*/true);
|
emitValue(result, /*rank=*/0, /*isPtr=*/true);
|
||||||
emitValue(result, /*isPtr=*/true);
|
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
emitError(func, "doesn't have a return operation as terminator.");
|
emitError(func, "doesn't have a return operation as terminator.");
|
||||||
|
|
Loading…
Reference in New Issue