.. _program_listing_file_lib_Dialect_EmitHLS_IR_EmitHLSOps.cpp: Program Listing for File EmitHLSOps.cpp ======================================= |exhale_lsh| :ref:`Return to documentation for file ` (``lib/Dialect/EmitHLS/IR/EmitHLSOps.cpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp #include "laksa-mlir/Dialect/EmitHLS/IR/EmitHLSOps.h" #include "laksa-mlir/Dialect/EmitHLS/EmitHLSEnums.h" #include "laksa-mlir/Dialect/EmitHLS/IR/EmitHLSBase.h" #include "laksa-mlir/Dialect/EmitHLS/IR/EmitHLSTypes.h" #include "mlir/IR/Builders.h" #include "mlir/IR/BuiltinTypes.h" #include "mlir/IR/IRMapping.h" #include "mlir/IR/OpDefinition.h" #include "mlir/IR/Operation.h" #include "mlir/IR/TypeUtilities.h" #include "mlir/Interfaces/FunctionImplementation.h" #include "mlir/Transforms/InliningUtils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace mlir; using namespace mlir::emithls; //===- Generated implementation -------------------------------------------===// #define GET_OP_CLASSES #include "laksa-mlir/Dialect/EmitHLS/IR/EmitHLSOps.cpp.inc" //===----------------------------------------------------------------------===// // For multiple variadic attributes constexpr char kOperandSegmentSizesAttr[] = "operandSegmentSizes"; //===----------------------------------------------------------------------===// // Structure operations //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// // IncludeOp //===----------------------------------------------------------------------===// void IncludeOp::build( OpBuilder &builder, OperationState &state, StringRef header) { state.addAttribute( getHeaderAttrName(state.name), builder.getStringAttr(header)); } ParseResult IncludeOp::parse(OpAsmParser &parser, OperationState &state) { StringAttr includeStr; OptionalParseResult includeParseResult = parser.parseOptionalAttribute( includeStr, getHeaderAttrName(state.name), state.attributes); if (!includeParseResult.has_value()) return parser.emitError(parser.getNameLoc()) << "expected string attribute"; return success(); } void IncludeOp::print(OpAsmPrinter &p) { p << " \"" << getHeader() << "\""; } LogicalResult IncludeOp::verify() { auto parent = getOperation()->getParentOp(); if (!isa(parent)) return emitOpError( "An IncludeOp must only have ModuleOp as its parent."); return success(); } namespace { struct RemoveSameInclude final : public OpRewritePattern { using OpRewritePattern::OpRewritePattern; LogicalResult matchAndRewrite(IncludeOp op, PatternRewriter &rewriter) const override { auto moduleOp = op->getParentOfType(); for (auto &bodyOp : moduleOp.getBody()->getOperations()) { if (&bodyOp == op.getOperation()) return failure(); if (auto prevInclude = dyn_cast(&bodyOp)) if (prevInclude.getHeader() == op.getHeader()) { rewriter.eraseOp(op); return success(); } } return failure(); } }; struct HoistIncludeToTop final : public OpRewritePattern { using OpRewritePattern::OpRewritePattern; LogicalResult matchAndRewrite(IncludeOp op, PatternRewriter &rewriter) const override { auto moduleOp = op->getParentOfType(); Block* moduleBody = moduleOp.getBody(); Operation* firstNonInclude = nullptr; for (auto &opInModule : moduleBody->getOperations()) { if (!isa(&opInModule)) { firstNonInclude = &opInModule; break; } } // If there is no other operation if (!firstNonInclude) return failure(); for (auto &opInModule : moduleBody->getOperations()) { if (&opInModule == op.getOperation()) return failure(); if (&opInModule == firstNonInclude) break; } rewriter.moveOpBefore(op, firstNonInclude); return success(); } }; struct ReorderIncludes final : public OpRewritePattern { using OpRewritePattern::OpRewritePattern; LogicalResult matchAndRewrite(IncludeOp op, PatternRewriter &rewriter) const override { auto moduleOp = op->getParentOfType(); SmallVector includes( moduleOp.getBody()->getOps()); if (includes.size() <= 1) return failure(); auto currentIdx = std::distance(includes.begin(), llvm::find(includes, op)); auto sortedIdx = llvm::count_if(includes, [&](IncludeOp includeOp) { return includeOp != op && includeOp.getHeader() < op.getHeader(); }); if (currentIdx == sortedIdx) return failure(); else if (currentIdx < sortedIdx) rewriter.moveOpAfter(op, includes[sortedIdx]); else rewriter.moveOpBefore(op, includes[sortedIdx]); return success(); } }; } // namespace void IncludeOp::getCanonicalizationPatterns( RewritePatternSet &patterns, MLIRContext* context) { patterns.add(context); patterns.add(context); patterns.add(context); } //===----------------------------------------------------------------------===// // FuncOp //===----------------------------------------------------------------------===// void FuncOp::build( OpBuilder &builder, OperationState &state, StringRef name, FunctionType functionType) { build(builder, state, name, functionType, ArrayAttr{}, ArrayAttr{}); } ParseResult FuncOp::parse(OpAsmParser &parser, OperationState &state) { auto buildFuncType = [](Builder &builder, ArrayRef argTypes, ArrayRef results, function_interface_impl::VariadicFlag, std::string &) { return builder.getFunctionType(argTypes, results); }; return function_interface_impl::parseFunctionOp( parser, state, /*allowVariadic=*/false, getFunctionTypeAttrName(state.name), buildFuncType, getArgAttrsAttrName(state.name), getResAttrsAttrName(state.name)); } void FuncOp::print(OpAsmPrinter &p) { function_interface_impl::printFunctionOp( p, *this, /*isVariadic=*/false, getFunctionTypeAttrName(), getArgAttrsAttrName(), getResAttrsAttrName()); } LogicalResult FuncOp::verify() { auto funcTy = getFunctionType(); if (funcTy.getNumInputs() != 0) { for (auto type : funcTy.getInputs()) if (auto arrayType = dyn_cast(type)) { auto arrayElemType = arrayType.getElementType(); if (isa(arrayElemType)) return emitOpError("Array of pointer type is not allowed."); } else if (!isa(type)) { return emitOpError("Unsupported type ") << type << ", needs to be stream/pointer/array type for " "function arguments."; } } return success(); } //===----------------------------------------------------------------------===// // CallOp //===----------------------------------------------------------------------===// void CallOp::build( OpBuilder &, OperationState &state, FuncOp callee, ValueRange operands) { state.addOperands(operands); state.addAttribute( getCalleeAttrName(state.name), SymbolRefAttr::get(callee)); state.addTypes(callee.getFunctionType().getResults()); } //===----------------------------------------------------------------------===// // VariableOp //===----------------------------------------------------------------------===// void VariableOp::getAsmResultNames( function_ref setNameFn) { auto thisOp = getOperation(); // Count sibling variables within the nearest enclosing FuncOp when there // is one; otherwise fall back to the nearest ancestor op that sits // directly under the module. Operation* scopeOp = thisOp->getParentOfType(); if (!scopeOp) { scopeOp = thisOp; while (Operation* parent = scopeOp->getParentOp()) { if (isa(parent)) break; scopeOp = parent; } if (scopeOp == thisOp || scopeOp->getNumRegions() == 0) return; } auto resultTy = getType(); unsigned count = 0; for (auto &opi : scopeOp->getRegion(0).front()) { if (auto varOp = dyn_cast(opi)) { if (varOp.getOperation() == thisOp) break; if (((varOp.getType() == resultTy) || (isa(varOp.getType()) && isa(resultTy))) && (varOp.getIsConst() == getIsConst())) ++count; } } std::string prefix = getIsConst() ? "const" : "var"; std::string suffix; if (isa(resultTy)) suffix = "_int" + std::to_string(resultTy.getIntOrFloatBitWidth()) + "_"; else if (isa(resultTy)) suffix = "_index_"; else if (isa(resultTy)) suffix = "_array_"; else if (isa(resultTy)) suffix = "_stream_"; setNameFn(getVariable(), prefix + suffix + std::to_string(count)); } void VariableOp::build( OpBuilder &builder, OperationState &state, Type type, Attribute initNumber, bool is_const) { state.addTypes(type); if (initNumber) state.addAttribute(getInitNumberAttrName(state.name), initNumber); if (is_const) state.addAttribute("is_const", builder.getUnitAttr()); } void VariableOp::build( OpBuilder &builder, OperationState &state, Type type, Value initValue, bool is_const) { state.addTypes(type); if (initValue) state.addOperands(initValue); if (is_const) state.addAttribute("is_const", builder.getUnitAttr()); } ParseResult VariableOp::parse(OpAsmParser &parser, OperationState &state) { if (failed(parser.parseKeyword("as"))) return parser.emitError( parser.getCurrentLocation(), "expected keyword 'as'"); // If it's const value bool isConst = false; if (succeeded(parser.parseOptionalKeyword("const"))) { state.addAttribute("is_const", parser.getBuilder().getUnitAttr()); isConst = true; } // Parse type Type type; if (failed(parser.parseType(type))) return parser.emitError(parser.getCurrentLocation(), "expected type"); state.addTypes(type); Attribute initNumber; OpAsmParser::UnresolvedOperand initValue; if (succeeded(parser.parseOptionalEqual())) { auto operandParseResult = parser.parseOptionalOperand(initValue); if (operandParseResult.has_value()) { // If there is an initial value if (failed(*operandParseResult)) return failure(); if (failed(parser.resolveOperand(initValue, type, state.operands))) return failure(); } else if ( failed(parser.parseAttribute( initNumber, type, getInitNumberAttrName(state.name).data(), state.attributes))) { // If there is an initial attribute return parser.emitError( parser.getCurrentLocation(), "expected initial attribute"); } } else { if (isConst) return parser.emitError( parser.getCurrentLocation(), "const value must have initial attribute"); } if (failed(parser.parseOptionalAttrDict(state.attributes))) return failure(); return success(); } void VariableOp::print(OpAsmPrinter &p) { p << " as "; if (getIsConst()) p << "const "; p << getType(); if (getInitNumber()) { p << " = "; p.printAttributeWithoutType(getInitNumberAttr()); } else if (getInitValue()) { p << " = " << getInitValue(); } p.printOptionalAttrDict( (*this)->getAttrs(), {getIsConstAttrName(), getInitNumberAttrName()}); } LogicalResult VariableOp::verify() { auto resultType = getType(); if (isa(resultType)) return emitOpError("Dynamic memory usage is not supported in HLS."); if (getInitNumber() && !isa(resultType)) return emitOpError("Unsupported type to have init value."); return success(); } namespace { struct RemoveUnusedVariable final : public OpRewritePattern { using OpRewritePattern::OpRewritePattern; LogicalResult matchAndRewrite(VariableOp op, PatternRewriter &rewriter) const override { if (op.getVariable().use_empty()) { rewriter.eraseOp(op); return success(); } return failure(); } }; struct RemoveSameConstVariable final : public OpRewritePattern { using OpRewritePattern::OpRewritePattern; LogicalResult matchAndRewrite(VariableOp op, PatternRewriter &rewriter) const override { if (!op.getIsConst()) return failure(); auto initAttr = op.getInitNumberAttr(); auto constTy = op.getType(); // Dedupe within the nearest enclosing FuncOp when there is one; // otherwise fall back to the nearest ancestor op that sits directly // under the module. Operation* scopeOp = op->getParentOfType(); if (!scopeOp) { scopeOp = op; while (Operation* parent = scopeOp->getParentOp()) { if (isa(parent)) break; scopeOp = parent; } if (scopeOp == op.getOperation()) return failure(); } VariableOp candidate; scopeOp->walk([&](VariableOp varOp) -> WalkResult { if (varOp == op) return WalkResult::interrupt(); if (varOp.getIsConst() && varOp->getParentOp() == scopeOp && varOp.getInitNumberAttr() == initAttr && varOp.getType() == constTy) candidate = varOp; return WalkResult::advance(); }); if (!candidate) return failure(); op.getResult().replaceAllUsesWith(candidate.getResult()); rewriter.eraseOp(op); return success(); } }; struct HoistToFunctionTop final : public OpRewritePattern { using OpRewritePattern::OpRewritePattern; LogicalResult matchAndRewrite(VariableOp op, PatternRewriter &) const override { if (!op.getIsConst()) return failure(); if (auto parentFunc = op->getParentOfType()) { if (isa(op->getParentOp())) return failure(); auto &entryBlock = parentFunc.getRegion().front(); op->moveBefore(&entryBlock, entryBlock.begin()); return success(); } // Hoist to the top of the nearest ancestor op that sits directly under // the module Operation* topLevelOp = op; while (Operation* parent = topLevelOp->getParentOp()) { if (isa(parent)) break; topLevelOp = parent; } if (topLevelOp == op.getOperation() || topLevelOp->getNumRegions() == 0) return failure(); if (op->getParentOp() == topLevelOp) return failure(); auto &entryBlock = topLevelOp->getRegion(0).front(); op->moveBefore(&entryBlock, entryBlock.begin()); return success(); } }; } // namespace void VariableOp::getCanonicalizationPatterns( RewritePatternSet &patterns, MLIRContext* context) { patterns.add(context); patterns.add(context); patterns.add(context); } //===----------------------------------------------------------------------===// // UpdateOp //===----------------------------------------------------------------------===// void UpdateOp::build( OpBuilder &builder, OperationState &state, Value variable, Value newValue, SmallVector indices, SmallVector newValueIndices) { state.addOperands(variable); if (!indices.empty()) state.addOperands(indices); state.addOperands(newValue); if (!newValueIndices.empty()) state.addOperands(newValueIndices); auto operandSegmentSizes = builder.getDenseI32ArrayAttr({/*variable*/ 1, int32_t(indices.size()), /*newValue*/ 1, int32_t(newValueIndices.size())}); state.addAttribute(kOperandSegmentSizesAttr, operandSegmentSizes); } ParseResult UpdateOp::parse(OpAsmParser &parser, OperationState &state) { OpAsmParser::UnresolvedOperand variable, newValue; SmallVector indices, newValueIndices; if (parser.parseOperand(variable)) return failure(); if (succeeded(parser.parseOptionalLSquare())) { if (parser.parseCommaSeparatedList([&]() -> ParseResult { OpAsmParser::UnresolvedOperand idx; if (parser.parseOperand(idx)) return failure(); indices.push_back(idx); return success(); }) || parser.parseRSquare()) return failure(); } if (parser.parseKeyword("with") || parser.parseOperand(newValue)) return failure(); if (succeeded(parser.parseOptionalLSquare())) { if (parser.parseCommaSeparatedList([&]() -> ParseResult { OpAsmParser::UnresolvedOperand idx; if (parser.parseOperand(idx)) return failure(); newValueIndices.push_back(idx); return success(); }) || parser.parseRSquare()) return failure(); } Type variableTy, newValueTy; if (parser.parseColon() || parser.parseType(variableTy) || parser.parseLess() || parser.parseMinus() || parser.parseType(newValueTy)) return failure(); if (parser.resolveOperand(variable, variableTy, state.operands)) return failure(); if (!indices.empty()) { for (auto idx : indices) if (parser.resolveOperand( idx, parser.getBuilder().getIndexType(), state.operands)) return failure(); } if (parser.resolveOperand(newValue, newValueTy, state.operands)) return failure(); if (!newValueIndices.empty()) { for (auto idx : newValueIndices) if (parser.resolveOperand( idx, parser.getBuilder().getIndexType(), state.operands)) return failure(); } auto operandSegmentSizes = parser.getBuilder().getDenseI32ArrayAttr( {/*variable*/ 1, int32_t(indices.size()), /*newValue*/ 1, int32_t(newValueIndices.size())}); state.addAttribute(kOperandSegmentSizesAttr, operandSegmentSizes); return success(); } void UpdateOp::print(OpAsmPrinter &p) { p << " " << getVariable(); auto indices = getIndices(); if (!indices.empty()) { p << "["; for (size_t i = 0; i < indices.size(); ++i) { p << indices[i]; if (i < indices.size() - 1) p << ", "; } p << "]"; } p << " with " << getNewValue(); auto newValueIndices = getNewValueIndices(); if (!newValueIndices.empty()) { p << "["; for (size_t i = 0; i < newValueIndices.size(); ++i) { p << newValueIndices[i]; if (i < newValueIndices.size() - 1) p << ", "; } p << "]"; } p << " : " << getVariable().getType() << " <- " << getNewValue().getType(); } LogicalResult UpdateOp::verify() { auto variable = getVariable(); Type varTy = getIndices().empty() ? variable.getType() : dyn_cast(variable.getType()).getElementType(); auto newValue = getNewValue(); Type newValTy = getNewValueIndices().empty() ? newValue.getType() : dyn_cast(newValue.getType()).getElementType(); if (varTy != newValTy) return emitOpError("update value and variable types don't match"); return success(); } //===----------------------------------------------------------------------===// // ExpressionOp //===----------------------------------------------------------------------===// void ExpressionOp::build( OpBuilder &builder, OperationState &state, Type resultType, function_ref bodyBuilder) { OpBuilder::InsertionGuard guard(builder); Region* bodyRegion = state.addRegion(); builder.createBlock(bodyRegion); bodyBuilder(builder, state.location); state.addTypes(resultType); } void ExpressionOp::getAsmResultNames( function_ref setNameFn) { Operation* self = getOperation(); Operation* topLevel = self; while (Operation* p = topLevel->getParentOp()) { if (isa(p)) break; topLevel = p; } unsigned count = 0; topLevel->walk([&](ExpressionOp op) -> WalkResult { if (op.getOperation() == self) return WalkResult::interrupt(); ++count; return WalkResult::advance(); }); setNameFn(getResult(), "expr" + std::to_string(count)); } LogicalResult ExpressionOp::verify() { auto yieldOp = cast((&getBody().front())->getTerminator()); if (getType() != yieldOp.getValue().getType()) return emitOpError( "Expression return a different type from yield value."); return success(); } namespace { struct InlinePureNestedExpression final : public OpRewritePattern { using OpRewritePattern::OpRewritePattern; LogicalResult matchAndRewrite(ExpressionOp op, PatternRewriter &rewriter) const override { Block* block = &op.getBody().front(); // Match: body must be exactly an ExpressionOp followed by a YieldOp. if (std::ranges::distance(block->getOperations()) != 2) return failure(); auto innerExpr = dyn_cast(block->front()); if (!innerExpr) return failure(); // Splice the inner expression's body (including its YieldOp) before it, // then erase the now-redundant wrapper and the outer YieldOp. rewriter.setInsertionPointToStart(block); IRMapping mapper; for (auto &opi : innerExpr.getBody().front()) rewriter.clone(opi, mapper); rewriter.eraseOp(&block->back()); // outer YieldOp rewriter.eraseOp(innerExpr); return success(); } }; } // namespace void ExpressionOp::getCanonicalizationPatterns( RewritePatternSet &patterns, MLIRContext* context) { patterns.add(context); } //===----------------------------------------------------------------------===// // YieldOp //===----------------------------------------------------------------------===// void YieldOp::build(OpBuilder &, OperationState &state, Value value) { state.addOperands(value); } void YieldOp::build(OpBuilder &, OperationState &) { // Do nothing } //===----------------------------------------------------------------------===// // IfOp //===----------------------------------------------------------------------===// void IfOp::build( OpBuilder &builder, OperationState &state, Value condition, function_ref thenBuilder, function_ref elseBuilder) { state.addOperands(condition); OpBuilder::InsertionGuard guard(builder); Region* thenRegion = state.addRegion(); builder.createBlock(thenRegion); thenBuilder(builder, state.location); Region* elseRegion = state.addRegion(); if (elseBuilder) { builder.createBlock(elseRegion); elseBuilder(builder, state.location); } } void IfOp::build( OpBuilder &builder, OperationState &state, Value condition, bool ensureElseBlock) { state.addOperands(condition); OpBuilder::InsertionGuard guard(builder); Region* thenRegion = state.addRegion(); builder.createBlock(thenRegion); Region* elseRegion = state.addRegion(); if (ensureElseBlock) builder.createBlock(elseRegion); } ParseResult IfOp::parse(OpAsmParser &parser, OperationState &state) { state.regions.reserve(2); Region* thenRegion = state.addRegion(); Region* elseRegion = state.addRegion(); OpAsmParser::UnresolvedOperand condition; if (parser.parseOperand(condition) || parser.resolveOperand( condition, parser.getBuilder().getI1Type(), state.operands)) return failure(); if (parser.parseRegion(*thenRegion, {}, {})) return failure(); if (succeeded(parser.parseOptionalKeyword("else"))) if (parser.parseRegion(*elseRegion, {}, {})) return failure(); return success(); } void IfOp::print(OpAsmPrinter &p) { p << " " << getCondition() << " "; p.printRegion(getThenRegion(), false, false); auto &elseRegion = getElseRegion(); if (!elseRegion.empty()) { p << " else "; p.printRegion(elseRegion, false, false); } } //===----------------------------------------------------------------------===// // ForOp //===----------------------------------------------------------------------===// void ForOp::build( OpBuilder &builder, OperationState &state, int64_t lowerBound, int64_t upperBound, int64_t step, function_ref bodyBuilder) { state.addAttribute( getLowerBoundAttrName(state.name), builder.getIntegerAttr(builder.getIndexType(), lowerBound)); state.addAttribute( getUpperBoundAttrName(state.name), builder.getIntegerAttr(builder.getIndexType(), upperBound)); state.addAttribute( getStepAttrName(state.name), builder.getIntegerAttr(builder.getIndexType(), step)); OpBuilder::InsertionGuard guard(builder); Region* bodyRegion = state.addRegion(); Block* bodyBlock = builder.createBlock(bodyRegion); bodyBlock->addArgument(builder.getIndexType(), state.location); if (bodyBuilder) bodyBuilder(builder, state.location, bodyBlock->getArguments()); } void ForOp::build( OpBuilder &builder, OperationState &state, Attribute lowerBound, Attribute upperBound, Attribute step, function_ref bodyBuilder) { state.addAttribute(getLowerBoundAttrName(state.name), lowerBound); state.addAttribute(getUpperBoundAttrName(state.name), upperBound); state.addAttribute(getStepAttrName(state.name), step); OpBuilder::InsertionGuard g(builder); Region* bodyRegion = state.addRegion(); Block* bodyBlock = builder.createBlock(bodyRegion); bodyBlock->addArgument(builder.getIndexType(), state.location); if (bodyBuilder) bodyBuilder(builder, state.location, bodyBlock->getArguments()); } void ForOp::getAsmBlockArgumentNames( Region ®ion, OpAsmSetValueNameFn setNameFn) { Operation* forOp = getOperation(); unsigned nestedLevel = 0; while ((forOp = forOp->getParentOp()) && forOp) if (isa(forOp)) ++nestedLevel; setNameFn(region.getArgument(0), "idx" + std::to_string(nestedLevel)); } ParseResult ForOp::parse(OpAsmParser &parser, OperationState &state) { auto &builder = parser.getBuilder(); Type indexType = builder.getIndexType(); OpAsmParser::Argument inductionVariable; if (parser.parseOperand(inductionVariable.ssaName) || parser.parseEqual()) return failure(); IntegerAttr lbAttr, ubAttr, stepAttr; if (parser.parseAttribute( lbAttr, indexType, getLowerBoundAttrName(state.name).data(), state.attributes) || parser.parseKeyword("to") || parser.parseAttribute( ubAttr, indexType, getUpperBoundAttrName(state.name).data(), state.attributes) || parser.parseKeyword("step") || parser.parseAttribute( stepAttr, indexType, getStepAttrName(state.name).data(), state.attributes)) return failure(); SmallVector blkArgs; blkArgs.push_back(inductionVariable); blkArgs.front().type = indexType; Region* body = state.addRegion(); if (parser.parseRegion(*body, blkArgs)) return failure(); if (parser.parseOptionalAttrDict(state.attributes)) return failure(); return success(); } void ForOp::print(OpAsmPrinter &p) { p << " " << getInductionVariable() << " = " << getLowerBound() << " to " << getUpperBound() << " step " << getStep(); p << ' '; p.printRegion( getRegion(), /*printEntryBlockArgs=*/false, /*printBlockTerminators=*/false); p.printOptionalAttrDict( (*this)->getAttrs(), {getLowerBoundAttrName(), getUpperBoundAttrName(), getStepAttrName()}); } LogicalResult ForOp::verify() { if (getLowerBound() == getUpperBound()) return emitOpError("expected loop iterate at least once"); if (getStep() == 0) return emitOpError("expected non-zero step"); if (getUpperBound().slt(getLowerBound() + getStep())) return emitOpError("unexpected index overflow"); return success(); } namespace { struct RemoveTrivialLoop : public OpRewritePattern { using OpRewritePattern::OpRewritePattern; LogicalResult matchAndRewrite(ForOp op, PatternRewriter &rewriter) const override { if ((op.getLowerBound() + op.getStep()) != op.getUpperBound()) return failure(); Block &body = op.getRegion().front(); bool hasPipeline = !body.empty() && isa(body.front()); auto parent = dyn_cast(op->getParentOp()); bool siblingHasLoop = parent && llvm::any_of(parent.getBody().front(), [&](Operation &sibling) { return &sibling != op.getOperation() && sibling .walk([](ForOp) { return WalkResult::interrupt(); }) .wasInterrupted(); }); if (hasPipeline && (!parent || siblingHasLoop)) return failure(); if (hasPipeline) rewriter.moveOpBefore( &body.front(), &parent.getBody().front(), parent.getBody().front().begin()); rewriter.setInsertionPoint(op); IRMapping mapper; auto constVarOp = VariableOp::create( rewriter, op.getLoc(), rewriter.getIndexType(), op.getLowerBoundAttr(), true); mapper.map(op.getInductionVariable(), constVarOp.getResult()); for (auto &opFor : op.getRegion().getOps()) rewriter.clone(opFor, mapper); rewriter.eraseOp(op); return success(); } }; struct RemoveEmptyLoop : public OpRewritePattern { using OpRewritePattern::OpRewritePattern; LogicalResult matchAndRewrite(ForOp op, PatternRewriter &rewriter) const override { // Preserve loops whose body is not empty if (!op.getRegion().getOps().empty()) return failure(); rewriter.eraseOp(op); return success(); } }; struct CollapseUnusedOuterLoop : public OpRewritePattern { using OpRewritePattern::OpRewritePattern; LogicalResult matchAndRewrite(ForOp op, PatternRewriter &rewriter) const override { // Only safe when NEITHER induction variable is read if (!op.getInductionVariable().use_empty()) return failure(); Block &body = op.getRegion().front(); if (body.empty() || &body.front() != &body.back()) return failure(); auto inner = dyn_cast(body.front()); if (!inner || !inner.getInductionVariable().use_empty()) return failure(); rewriter.setInsertionPoint(op); auto merged = ForOp::create( rewriter, op.getLoc(), 0, op.getTripCount() * inner.getTripCount()); // Move inner's body into merged rewriter.mergeBlocks( &inner.getRegion().front(), &merged.getRegion().front(), merged.getInductionVariable()); rewriter.eraseOp(op); return success(); } }; } // namespace void ForOp::getCanonicalizationPatterns( RewritePatternSet &results, MLIRContext* context) { results.add(context); } //===----------------------------------------------------------------------===// // Arithmetic operations //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// // ArithFusedOp //===----------------------------------------------------------------------===// LogicalResult ArithFusedOp::verify() { if (auto varOp = dyn_cast(getAcc().getDefiningOp())) { if (varOp.getIsConst()) return emitOpError("Accumulator cannot be a constant variable."); } else { return emitOpError("Accumulator of the fused op must be a variable."); } return success(); } //===----------------------------------------------------------------------===// // ArithDataRangeOp //===----------------------------------------------------------------------===// ParseResult ArithDataRangeOp::parse(OpAsmParser &parser, OperationState &state) { OpAsmParser::UnresolvedOperand data, hBit, lBit; Type dataTy, outTy; Type indexTy = parser.getBuilder().getIndexType(); if (parser.parseOperand(data) || parser.parseLParen() || parser.parseOperand(hBit) || parser.parseComma() || parser.parseOperand(lBit) || parser.parseRParen()) return failure(); if (parser.parseColon() || parser.parseType(dataTy) || parser.parseArrow() || parser.parseType(outTy)) return failure(); if (parser.resolveOperand(data, dataTy, state.operands) || parser.resolveOperand(hBit, indexTy, state.operands) || parser.resolveOperand(lBit, indexTy, state.operands)) return failure(); state.addTypes(outTy); return success(); } void ArithDataRangeOp::print(OpAsmPrinter &p) { p << " " << getData() << "(" << getHighBit() << ", " << getLowBit() << ")"; p << " : " << getData().getType() << " -> " << getType(); } //===----------------------------------------------------------------------===// // Array operations //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// // ArrayReadOp //===----------------------------------------------------------------------===// LogicalResult ArrayReadOp::verify() { if (static_cast(getIndices().size()) != getArrayType().getRank()) { return emitOpError( "incorrect number of indices for array read, expected ") << getArrayType().getRank() << " but got " << getIndices().size(); } return success(); } //===----------------------------------------------------------------------===// // ArrayWriteOp //===----------------------------------------------------------------------===// LogicalResult ArrayWriteOp::verify() { if (static_cast(getIndices().size()) != getArrayType().getRank()) return emitOpError( "array write index operand count not equal to memref rank"); return success(); } //===----------------------------------------------------------------------===// // Stream operations //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// // StreamReadOp //===----------------------------------------------------------------------===// void StreamReadOp::build( OpBuilder &, OperationState &state, Type resultType, Value stream, ValueRange indices) { state.addOperands(stream); if (!indices.empty()) state.addOperands(indices); state.addTypes(resultType); } LogicalResult StreamReadOp::verify() { auto streamType = getStream().getType(); auto indices = getIndices(); Type elemType; if (auto arrayType = dyn_cast(streamType)) { // If the stream is an array of streams auto arrayShape = arrayType.getShape(); auto arrayElemType = arrayType.getElementType(); if (!isa(arrayElemType)) return emitOpError("expect array of streams"); elemType = cast(arrayElemType).getElementType(); // Then, check the indices if (indices.empty()) return emitOpError("expect indices"); if (indices.size() != arrayShape.size()) return emitOpError("indices and array shapes mismatch"); } else { // Otherwise, it must be a stream type elemType = cast(streamType).getElementType(); // Expect no indices if (!indices.empty()) return emitOpError("expect no indices"); } if (elemType != getType()) emitOpError("result and stream types mismatch"); return success(); } //===----------------------------------------------------------------------===// // StreamWriteOp //===----------------------------------------------------------------------===// void StreamWriteOp::build( OpBuilder &, OperationState &state, Value data, Value stream, ValueRange indices) { state.addOperands(data); state.addOperands(stream); if (!indices.empty()) state.addOperands(indices); } LogicalResult StreamWriteOp::verify() { auto streamType = getStream().getType(); auto indices = getIndices(); Type elemType; if (auto arrayType = dyn_cast(streamType)) { // If the stream is an array of streams auto arrayShape = arrayType.getShape(); auto arrayElemType = arrayType.getElementType(); if (!isa(arrayElemType)) return emitOpError("expect array of streams"); elemType = cast(arrayElemType).getElementType(); // Then, check the indices if (indices.empty()) return emitOpError("expect indices"); if (indices.size() != arrayShape.size()) return emitOpError("indices and array shapes mismatch"); } else { // Otherwise, it must be a stream type elemType = cast(streamType).getElementType(); // Expect no indices if (!indices.empty()) return emitOpError("expect no indices"); } if (elemType != getData().getType()) return emitOpError("data and stream types mismatch"); return success(); } //===----------------------------------------------------------------------===// // Pragma operations //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// // PragmaArrayPartitionOp //===----------------------------------------------------------------------===// void PragmaArrayPartitionOp::build( OpBuilder &builder, OperationState &state, Value variable, ArrayPartitionType partType, std::optional partFactor, std::optional partDim) { state.addOperands(variable); state.addAttribute( getPartTypeAttrName(state.name), ArrayPartitionTypeAttr::get(builder.getContext(), partType)); if (partFactor.has_value()) { state.addAttribute( getPartFactorAttrName(state.name), builder.getI32IntegerAttr(partFactor.value())); } if (partDim.has_value()) { state.addAttribute( getPartDimAttrName(state.name), builder.getI32IntegerAttr(partDim.value())); } } ParseResult PragmaArrayPartitionOp::parse(OpAsmParser &parser, OperationState &state) { OpAsmParser::UnresolvedOperand variable; Type variableType; if (parser.parseKeyword("variable") || parser.parseEqual() || parser.parseOperand(variable) || parser.parseLParen() || parser.parseType(variableType) || parser.parseRParen()) return failure(); if (parser.resolveOperand(variable, variableType, state.operands)) return failure(); StringRef partTypeStr; if (parser.parseKeyword("type") || parser.parseEqual() || parser.parseKeyword(&partTypeStr)) return failure(); auto partType = symbolizeArrayPartitionType(partTypeStr); if (!partType.has_value()) return parser.emitError( parser.getNameLoc(), "unknown array partition type"); state.addAttribute( getPartTypeAttrName(state.name), ArrayPartitionTypeAttr::get(parser.getContext(), partType.value())); if (succeeded(parser.parseOptionalKeyword("factor"))) { int32_t partFactor; if (parser.parseEqual() || parser.parseInteger(partFactor)) return failure(); state.addAttribute( getPartFactorAttrName(state.name), parser.getBuilder().getI32IntegerAttr(partFactor)); } if (succeeded(parser.parseOptionalKeyword("dim"))) { int32_t partDim; if (parser.parseEqual() || parser.parseInteger(partDim)) return failure(); state.addAttribute( getPartDimAttrName(state.name), parser.getBuilder().getI32IntegerAttr(partDim)); } return success(); } void PragmaArrayPartitionOp::print(OpAsmPrinter &p) { p << " variable=" << getVariable() << "(" << getVariableType() << ")"; p << " type=" << stringifyArrayPartitionType(getPartType()); if (getPartFactor()) p << " factor=" << getPartFactor(); if (getPartDim()) p << " dim=" << getPartDim(); } LogicalResult PragmaArrayPartitionOp::verify() { auto partType = getPartType(); auto partFactor = getPartFactor(); auto partDim = getPartDim(); if (partType == ArrayPartitionType::cyclic || partType == ArrayPartitionType::block) { if (!partFactor.has_value()) return emitOpError("cyclic and block partition requires a factor"); if (!partDim.has_value()) return emitOpError("cyclic and block partition requires a dim"); } else { if (partFactor.has_value()) return emitOpError("complete partition needs no factor"); } return success(); } //===----------------------------------------------------------------------===// // PragmaBindStorageOp //===----------------------------------------------------------------------===// void PragmaBindStorageOp::build( OpBuilder &builder, OperationState &state, Value variable, BindStorageType storageType, BindStorageImpl storageImpl) { state.addOperands(variable); state.addAttribute( getStorageTypeAttrName(state.name), BindStorageTypeAttr::get(builder.getContext(), storageType)); state.addAttribute( getStorageImplAttrName(state.name), BindStorageImplAttr::get(builder.getContext(), storageImpl)); } ParseResult PragmaBindStorageOp::parse(OpAsmParser &parser, OperationState &state) { OpAsmParser::UnresolvedOperand variable; Type variableType; if (parser.parseKeyword("variable") || parser.parseEqual() || parser.parseOperand(variable) || parser.parseLParen() || parser.parseType(variableType) || parser.parseRParen()) return failure(); if (parser.resolveOperand(variable, variableType, state.operands)) return failure(); StringRef storageTypeStr; if (parser.parseKeyword("type") || parser.parseEqual() || parser.parseKeyword(&storageTypeStr)) return failure(); auto storageType = symbolizeBindStorageType(storageTypeStr); if (!storageType.has_value()) return parser.emitError( parser.getNameLoc(), "unknown bind storage type"); state.addAttribute( getStorageTypeAttrName(state.name), BindStorageTypeAttr::get( parser.getBuilder().getContext(), storageType.value())); StringRef storageImplStr; if (parser.parseKeyword("impl") || parser.parseEqual() || parser.parseKeyword(&storageImplStr)) return failure(); auto storageImpl = symbolizeBindStorageImpl(storageImplStr); if (!storageImpl.has_value()) return parser.emitError( parser.getNameLoc(), "unknown bind storage impl"); state.addAttribute( getStorageImplAttrName(state.name), BindStorageImplAttr::get( parser.getBuilder().getContext(), storageImpl.value())); return success(); } void PragmaBindStorageOp::print(OpAsmPrinter &p) { p << " variable=" << getVariable() << "(" << getVariableType() << ")"; p << " type=" << stringifyBindStorageType(getStorageType()); p << " impl=" << stringifyBindStorageImpl(getStorageImpl()); } //===----------------------------------------------------------------------===// // PragmaDataflowOp //===----------------------------------------------------------------------===// void PragmaDataflowOp::build( OpBuilder &builder, OperationState &state, function_ref bodyBuilder) { OpBuilder::InsertionGuard guard(builder); Region* dataflowRegion = state.addRegion(); if (bodyBuilder) { builder.createBlock(dataflowRegion); bodyBuilder(builder, state.location); } } //===----------------------------------------------------------------------===// // PragmaInlineOp //===----------------------------------------------------------------------===// void PragmaInlineOp::build(OpBuilder &builder, OperationState &state, bool off) { if (off) state.addAttribute(getOffAttrName(state.name), builder.getUnitAttr()); } ParseResult PragmaInlineOp::parse(OpAsmParser &parser, OperationState &state) { if (succeeded(parser.parseOptionalKeyword("off"))) state.addAttribute( getOffAttrName(state.name), parser.getBuilder().getUnitAttr()); return success(); } void PragmaInlineOp::print(OpAsmPrinter &p) { if (getOff()) p << " off"; } namespace { struct HoistPragmaInlinePattern : public OpRewritePattern { using OpRewritePattern::OpRewritePattern; LogicalResult matchAndRewrite(PragmaInlineOp op, PatternRewriter &) const override { Block* block = op->getBlock(); if (&block->front() == op.getOperation()) return failure(); op->moveBefore(&block->front()); return success(); } // Ensure this has higher priority over constant hoist PatternBenefit getBenefit() const { return 2; } }; } // namespace void PragmaInlineOp::getCanonicalizationPatterns( RewritePatternSet &patterns, MLIRContext* context) { patterns.add(context, 100); } //===----------------------------------------------------------------------===// // PragmaPipelineOp //===----------------------------------------------------------------------===// void PragmaPipelineOp::build( OpBuilder &builder, OperationState &result, int32_t interval, emithls::PipelineStyle style) { result.addAttribute( getIntervalAttrName(result.name), builder.getI32IntegerAttr(interval)); result.addAttribute( getStyleAttrName(result.name), emithls::PipelineStyleAttr::get(builder.getContext(), style)); } ParseResult PragmaPipelineOp::parse(OpAsmParser &parser, OperationState &state) { // Parse initial interval as integer int32_t interval; if (parser.parseKeyword("II") || parser.parseEqual() || parser.parseInteger(interval)) return failure(); state.addAttribute( getIntervalAttrName(state.name), parser.getBuilder().getI32IntegerAttr(interval)); // Parse style string, if none, use flp by default PipelineStyle style = PipelineStyle::flp; if (succeeded(parser.parseOptionalKeyword("style"))) { if (parser.parseEqual()) return failure(); StringRef styleStr; if (parser.parseKeyword(&styleStr)) return failure(); auto styleValue = symbolizePipelineStyle(styleStr); if (!styleValue) return parser.emitError( parser.getNameLoc(), "unknown pipeline style"); style = *styleValue; } state.addAttribute( getStyleAttrName(state.name), PipelineStyleAttr::get(parser.getContext(), style)); return success(); } void PragmaPipelineOp::print(OpAsmPrinter &p) { p << " II=" << getInterval() << " style=" << stringifyPipelineStyle(getStyle()); } LogicalResult PragmaPipelineOp::verify() { if (!isa(getOperation()->getParentOp())) return emitOpError("pipeline pragma must be in a for-loop"); if (getInterval() <= 0) return emitOpError("II must be greater than 0"); return success(); } namespace { struct RemoveNestedPipeline final : public OpRewritePattern { using OpRewritePattern::OpRewritePattern; LogicalResult matchAndRewrite( PragmaPipelineOp op, PatternRewriter &rewriter) const override { auto hasPrevPipeline = [](Operation* o) { auto* prev = o->getPrevNode(); return prev && isa(prev); }; // Check if there is pipeline before if (hasPrevPipeline(op)) { rewriter.eraseOp(op); return success(); } // Check if the parent loop is already pipelined for (Operation* parent = op->getParentOp(); parent; parent = parent->getParentOp()) { if (isa(parent) && hasPrevPipeline(parent)) { rewriter.eraseOp(op); return success(); } } return failure(); } }; } // namespace void PragmaPipelineOp::getCanonicalizationPatterns( RewritePatternSet &results, MLIRContext* context) { results.add(context); } //===----------------------------------------------------------------------===// // PragmaStreamOp //===----------------------------------------------------------------------===// void PragmaStreamOp::build( OpBuilder &builder, OperationState &state, Value variable, int32_t depth) { state.addOperands(variable); state.addAttribute( getDepthAttrName(state.name), builder.getI32IntegerAttr(depth)); } ParseResult PragmaStreamOp::parse(OpAsmParser &parser, OperationState &state) { OpAsmParser::UnresolvedOperand variable; Type variableType; if (parser.parseKeyword("variable") || parser.parseEqual() || parser.parseOperand(variable) || parser.parseLParen() || parser.parseType(variableType) || parser.parseRParen()) return failure(); if (parser.resolveOperand(variable, variableType, state.operands)) return failure(); int32_t depth; if (parser.parseKeyword("depth") || parser.parseEqual() || parser.parseInteger(depth)) return failure(); state.addAttribute( getDepthAttrName(state.name), parser.getBuilder().getI32IntegerAttr(depth)); return success(); } void PragmaStreamOp::print(OpAsmPrinter &p) { p << " variable=" << getVariable() << "(" << getVariableType() << ")"; p << " depth=" << getDepth(); } LogicalResult PragmaStreamOp::verify() { if (getDepth() <= 0) return emitOpError("expect positive integer depth"); if (auto arrayType = dyn_cast(getVariableType())) { if (!isa(arrayType.getElementType())) return emitOpError("expect array of streams"); } return success(); } //===----------------------------------------------------------------------===// // PragmaUnrollOp //===----------------------------------------------------------------------===// void PragmaUnrollOp::build( OpBuilder &builder, OperationState &state, std::optional factor) { if (factor.has_value()) state.addAttribute( getFactorAttrName(state.name), builder.getI32IntegerAttr(factor.value())); } ParseResult PragmaUnrollOp::parse(OpAsmParser &parser, OperationState &state) { if (succeeded(parser.parseOptionalKeyword("factor"))) { int32_t factor; if (parser.parseEqual() || parser.parseInteger(factor)) return failure(); state.addAttribute( getFactorAttrName(state.name), parser.getBuilder().getI32IntegerAttr(factor)); } return success(); } void PragmaUnrollOp::print(OpAsmPrinter &p) { if (getFactor()) p << " factor=" << getFactor(); } //===----------------------------------------------------------------------===// // Helper Operations //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// // HelperLineBufferOp //===----------------------------------------------------------------------===// void HelperLineBufferOp::getAsmResultNames( function_ref setNameFn) { Operation* self = getOperation(); Operation* topLevel = self; while (Operation* p = topLevel->getParentOp()) { if (isa(p)) break; topLevel = p; } unsigned count = 0; topLevel->walk([&](HelperLineBufferOp op) -> WalkResult { if (op.getOperation() == self) return WalkResult::interrupt(); ++count; return WalkResult::advance(); }); setNameFn(getBufRef(), "linebuf_" + std::to_string(count)); } void HelperLineBufferOp::build( OpBuilder &builder, OperationState &state, Value tokenRef, Value index, Type linebufType, ArrayRef numChan, int32_t numLine) { state.addOperands(tokenRef); if (index) state.addOperands(index); state.addAttribute( getNumChanAttrName(state.name), builder.getI32ArrayAttr(numChan)); state.addAttribute( getNumLineAttrName(state.name), builder.getI32IntegerAttr(numLine)); state.addTypes({linebufType}); } ParseResult HelperLineBufferOp::parse(OpAsmParser &parser, OperationState &state) { OpAsmParser::UnresolvedOperand tokenRef, index; bool hasKeepIndex = false; int32_t numLine; SmallVector numChan; Type tokenType, linebufType; if (parser.parseOperand(tokenRef) || parser.parseKeyword("num_chan") || parser.parseLSquare()) return failure(); if (parser.parseOptionalRSquare().failed()) { do { int32_t val; if (parser.parseInteger(val)) return failure(); numChan.push_back(val); } while (parser.parseOptionalComma().succeeded()); if (parser.parseRSquare()) return failure(); } if (parser.parseKeyword("num_line") || parser.parseInteger(numLine)) return failure(); if (succeeded(parser.parseOptionalKeyword("keep"))) { hasKeepIndex = true; if (parser.parseOperand(index)) return failure(); } if (parser.parseColon() || parser.parseType(tokenType) || parser.parseArrow() || parser.parseType(linebufType)) return failure(); if (parser.resolveOperand(tokenRef, tokenType, state.operands)) return failure(); if (hasKeepIndex) if (parser.resolveOperand( index, parser.getBuilder().getIndexType(), state.operands)) return failure(); state.addAttribute( getNumChanAttrName(state.name), parser.getBuilder().getI32ArrayAttr(numChan)); state.addAttribute( getNumLineAttrName(state.name), parser.getBuilder().getI32IntegerAttr(numLine)); state.addTypes({linebufType}); return success(); } void HelperLineBufferOp::print(OpAsmPrinter &p) { p << " " << getTokenRef() << " num_chan ["; llvm::interleaveComma(getNumChan(), p, [&](Attribute attr) { p << llvm::cast(attr).getInt(); }); p << "] num_line " << getNumLine(); if (getIndex()) p << " keep " << getIndex(); p << " : " << getTokenRef().getType() << " -> " << getBufRef().getType(); } //===----------------------------------------------------------------------===// // HelperWindowOp //===----------------------------------------------------------------------===// void HelperWindowOp::getAsmResultNames( function_ref setNameFn) { Operation* self = getOperation(); Operation* topLevel = self; while (Operation* p = topLevel->getParentOp()) { if (isa(p)) break; topLevel = p; } unsigned count = 0; topLevel->walk([&](HelperWindowOp op) -> WalkResult { if (op.getOperation() == self) return WalkResult::interrupt(); ++count; return WalkResult::advance(); }); setNameFn(getWinRef(), "window_" + std::to_string(count)); } void HelperWindowOp::build( OpBuilder &, OperationState &state, Value linebuf, ValueRange indices, Type windowType) { state.addOperands(linebuf); state.addOperands(indices); state.addTypes(windowType); } ParseResult HelperWindowOp::parse(OpAsmParser &parser, OperationState &state) { OpAsmParser::UnresolvedOperand linebuf; SmallVector indices; Type bufferType, windowType; if (parser.parseOperand(linebuf) || parser.parseKeyword("from") || parser.parseLSquare() || parser.parseCommaSeparatedList([&]() -> ParseResult { OpAsmParser::UnresolvedOperand idx; if (parser.parseOperand(idx)) return failure(); indices.push_back(idx); return success(); }) || parser.parseRSquare() || parser.parseColon() || parser.parseType(bufferType) || parser.parseArrow() || parser.parseType(windowType)) return failure(); if (parser.resolveOperand(linebuf, bufferType, state.operands)) return failure(); if (parser.resolveOperands( indices, parser.getBuilder().getIndexType(), state.operands)) return failure(); state.addTypes(windowType); return success(); } void HelperWindowOp::print(OpAsmPrinter &p) { p << " " << getBufRef() << " from ["; for (auto [i, index] : llvm::enumerate(getIndices())) { p << index; if (i == getIndices().size() - 1) break; p << ", "; } p << "] : " << getBufRef().getType() << " -> " << getWinRef().getType(); } //===----------------------------------------------------------------------===// // HelperAccumulateOp //===----------------------------------------------------------------------===// void HelperAccumulateOp::build( OpBuilder &builder, OperationState &state, Value accuRef, ValueRange indices, FusedOperator operatorCode, Value value) { state.addOperands(accuRef); state.addOperands(value); state.addOperands(indices); state.addAttribute( getOpCodeAttrName(state.name), FusedOperatorAttr::get(builder.getContext(), operatorCode)); } ParseResult HelperAccumulateOp::parse(OpAsmParser &parser, OperationState &state) { OpAsmParser::UnresolvedOperand accuRef, value; SmallVector indices; StringRef operatorCodeStr; Type type; if (parser.parseOperand(accuRef) || parser.parseKeyword("at") || parser.parseLSquare()) return failure(); if (failed(parser.parseOptionalRSquare())) { if (parser.parseCommaSeparatedList([&]() -> ParseResult { OpAsmParser::UnresolvedOperand idx; if (parser.parseOperand(idx)) return failure(); indices.push_back(idx); return success(); }) || parser.parseRSquare()) return failure(); } if (parser.parseKeyword(&operatorCodeStr) || parser.parseOperand(value) || parser.parseColonType(type)) return failure(); auto operatorCode = symbolizeFusedOperator(operatorCodeStr); if (!operatorCode.has_value()) return parser.emitError( parser.getNameLoc(), "unknown accumulate operator"); state.addAttribute( getOpCodeAttrName(state.name), FusedOperatorAttr::get(parser.getContext(), operatorCode.value())); auto shapedType = cast(type); Type valueType = shapedType.getElementType(); if (succeeded(parser.parseOptionalKeyword("cast")) && parser.parseType(valueType)) return failure(); if (parser.resolveOperand(accuRef, shapedType, state.operands)) return failure(); if (parser.resolveOperand(value, valueType, state.operands)) return failure(); if (parser.resolveOperands( indices, parser.getBuilder().getIndexType(), parser.getCurrentLocation(), state.operands)) return failure(); return success(); } void HelperAccumulateOp::print(OpAsmPrinter &p) { p << " " << getAccuRef() << " at ["; llvm::interleaveComma(getIndices(), p); p << "] " << stringifyFusedOperator(getOpCode()) << " " << getValue(); p << " : " << getAccuRef().getType(); Type valueType = getValue().getType(); if (valueType != cast(getAccuRef().getType()).getElementType()) p << " cast " << valueType; } //===----------------------------------------------------------------------===// // EmitHLSDialect //===----------------------------------------------------------------------===// void EmitHLSDialect::registerOps() { addOperations< #define GET_OP_LIST #include "laksa-mlir/Dialect/EmitHLS/IR/EmitHLSOps.cpp.inc" >(); }