19#include "llvm/ADT/STLExtras.h"
20#include "llvm/ADT/SmallVector.h"
21#include "llvm/ADT/TypeSwitch.h"
22#include "llvm/Support/Casting.h"
27#include "mlir/Dialect/EmitC/IR/EmitCDialect.cpp.inc"
33void EmitCDialect::initialize() {
36#include "mlir/Dialect/EmitC/IR/EmitC.cpp.inc"
39#define GET_TYPEDEF_LIST
40#include "mlir/Dialect/EmitC/IR/EmitCTypes.cpp.inc"
43#define GET_ATTRDEF_LIST
44#include "mlir/Dialect/EmitC/IR/EmitCAttributes.cpp.inc"
53 return emitc::ConstantOp::create(builder, loc, type, value);
59 emitc::YieldOp::create(builder, loc);
63 if (llvm::isa<emitc::OpaqueType>(type))
65 if (
auto ptrType = llvm::dyn_cast<emitc::PointerType>(type))
67 if (
auto arrayType = llvm::dyn_cast<emitc::ArrayType>(type)) {
68 auto elemType = arrayType.getElementType();
69 return !llvm::isa<emitc::ArrayType>(elemType) &&
74 if (llvm::isa<IntegerType>(type))
76 if (llvm::isa<FloatType>(type))
78 if (
auto tensorType = llvm::dyn_cast<TensorType>(type)) {
79 if (!tensorType.hasStaticShape()) {
82 auto elemType = tensorType.getElementType();
83 if (llvm::isa<emitc::ArrayType>(elemType)) {
88 if (
auto tupleType = llvm::dyn_cast<TupleType>(type)) {
89 return llvm::all_of(tupleType.getTypes(), [](
Type type) {
90 return !llvm::isa<emitc::ArrayType>(type) && isSupportedEmitCType(type);
97 if (
auto intType = llvm::dyn_cast<IntegerType>(type)) {
98 switch (intType.getWidth()) {
113 return llvm::isa<IndexType, emitc::OpaqueType>(type) ||
118 if (
auto floatType = llvm::dyn_cast<FloatType>(type)) {
119 switch (floatType.getWidth()) {
121 return llvm::isa<Float16Type, BFloat16Type>(type);
133 return isa<emitc::SignedSizeTType, emitc::SizeTType, emitc::PtrDiffTType>(
140 isa<emitc::PointerType>(type);
147 assert(op->
getNumResults() == 1 &&
"operation must have 1 result");
149 if (llvm::isa<emitc::OpaqueAttr>(value))
152 if (llvm::isa<StringAttr>(value))
154 <<
"string attributes are not supported, use #emitc.opaque instead";
157 if (
auto lType = dyn_cast<LValueType>(resultType))
158 resultType = lType.getValueType();
159 Type attrType = cast<TypedAttr>(value).getType();
164 if (resultType != attrType)
166 <<
"requires attribute to either be an #emitc.opaque attribute or "
168 << attrType <<
") to match the op's result type (" << resultType
179template <
class ArgType>
181 StringRef toParse, ArgType fmtArgs,
186 if (fmtArgs.empty()) {
187 items.push_back(toParse);
191 while (!toParse.empty()) {
192 size_t idx = toParse.find(
'{');
193 if (idx == StringRef::npos) {
195 items.push_back(toParse);
200 items.push_back(toParse.take_front(idx));
201 toParse = toParse.drop_front(idx);
204 if (toParse.size() < 2) {
205 return emitError() <<
"expected '}' after unescaped '{' at end of string";
208 char nextChar = toParse[1];
209 if (nextChar ==
'{') {
211 items.push_back(toParse.take_front(1));
212 toParse = toParse.drop_front(2);
215 if (nextChar ==
'}') {
217 toParse = toParse.drop_front(2);
222 return emitError() <<
"expected '}' after unescaped '{'";
233LogicalResult AddressOfOp::verify() {
234 emitc::LValueType referenceType = getReference().getType();
235 emitc::PointerType resultType = getResult().getType();
237 if (referenceType.getValueType() != resultType.getPointee())
238 return emitOpError(
"requires result to be a pointer to the type "
239 "referenced by operand");
248LogicalResult AddOp::verify() {
249 Type lhsType = getLhs().getType();
250 Type rhsType = getRhs().getType();
252 if (isa<emitc::PointerType>(lhsType) && isa<emitc::PointerType>(rhsType))
253 return emitOpError(
"requires that at most one operand is a pointer");
255 if ((isa<emitc::PointerType>(lhsType) &&
256 !isa<IntegerType, emitc::OpaqueType>(rhsType)) ||
257 (isa<emitc::PointerType>(rhsType) &&
258 !isa<IntegerType, emitc::OpaqueType>(lhsType)))
259 return emitOpError(
"requires that one operand is an integer or of opaque "
260 "type if the other is a pointer");
269template <
typename AssignmentOp>
273 if (!variable.getDefiningOp())
274 return op.emitOpError() <<
"cannot assign to block argument";
276 Type valueType = op.getValue().getType();
277 Type variableType = variable.getType().getValueType();
278 if (variableType != valueType)
279 return op.emitOpError() <<
"requires value's type (" << valueType
280 <<
") to match variable's type (" << variableType
281 <<
")\n variable: " << variable
282 <<
"\n value: " << op.getValue() <<
"\n";
303 Type input = inputs.front(), output = outputs.front();
305 if (
auto arrayType = dyn_cast<emitc::ArrayType>(input)) {
306 if (
auto pointerType = dyn_cast<emitc::PointerType>(output)) {
307 return (arrayType.getElementType() == pointerType.getPointee()) &&
308 arrayType.getShape().size() == 1 && arrayType.getShape()[0] >= 1;
324void emitc::CastOp::getEffects(
339 std::optional<ArrayAttr> args,
340 std::optional<ArrayAttr> templateArgs,
341 TypeRange resultTypes,
size_t numArgsOperands) {
344 return op->
emitOpError(
"callee must not be empty");
348 auto intAttr = llvm::dyn_cast<IntegerAttr>(arg);
349 if (intAttr && llvm::isa<IndexType>(intAttr.getType())) {
354 return op->
emitOpError(
"index argument is out of range");
356 }
else if (llvm::isa<ArrayAttr>(arg)) {
357 return op->
emitOpError(
"array argument has no type");
364 if (!llvm::isa<TypeAttr, IntegerAttr, FloatAttr, emitc::OpaqueAttr>(tArg))
365 return op->
emitOpError(
"template argument has invalid type");
369 if (llvm::any_of(resultTypes, llvm::IsaPred<ArrayType>)) {
370 return op->
emitOpError() <<
"cannot return array type";
376LogicalResult emitc::CallOpaqueOp::verify() {
378 getTemplateArgs(), getResultTypes(),
382LogicalResult emitc::MemberCallOpaqueOp::verify() {
384 getTemplateArgs(), getResultTypes(),
385 getArgOperands().size());
392LogicalResult emitc::ConstantOp::verify() {
396 if (
auto opaqueValue = llvm::dyn_cast<emitc::OpaqueAttr>(value)) {
397 if (opaqueValue.getValue().empty())
403OpFoldResult emitc::ConstantOp::fold(FoldAdaptor adaptor) {
return getValue(); }
409LogicalResult DereferenceOp::verify() {
410 emitc::PointerType pointerType = getPointer().getType();
413 return emitOpError(
"requires result to be an lvalue of the type "
414 "pointed to by operand");
425struct RemoveRecurringExpressionOperands
427 using OpRewritePattern<ExpressionOp>::OpRewritePattern;
428 LogicalResult matchAndRewrite(ExpressionOp expressionOp,
429 PatternRewriter &rewriter)
const override {
434 for (
auto [i, operand] : llvm::enumerate(expressionOp.getDefs())) {
435 if (uniqueOperands.contains(operand))
437 uniqueOperands.insert(operand);
438 firstIndexOf[operand] = i;
442 if (uniqueOperands.size() == expressionOp.getDefs().size())
447 auto uniqueExpression = emitc::ExpressionOp::create(
448 rewriter, expressionOp.getLoc(), expressionOp.getResult().getType(),
449 uniqueOperands.getArrayRef(), expressionOp.getDoNotInline());
450 Block &uniqueExpressionBody = uniqueExpression.createBody();
455 Block *expressionBody = expressionOp.getBody();
456 for (
auto [operand, arg] :
457 llvm::zip(expressionOp.getOperands(), expressionBody->
getArguments()))
458 mapper.
map(arg, uniqueExpressionBody.
getArgument(firstIndexOf[operand]));
461 for (Operation &opToClone : *expressionOp.getBody())
462 rewriter.
clone(opToClone, mapper);
465 rewriter.
replaceOp(expressionOp, uniqueExpression);
476 using OpRewritePattern<ExpressionOp>::OpRewritePattern;
477 LogicalResult matchAndRewrite(ExpressionOp expressionOp,
478 PatternRewriter &rewriter)
const override {
479 auto yieldOp = cast<YieldOp>(expressionOp.getBody()->getTerminator());
480 Value yieldedValue = yieldOp.getResult();
481 auto blockArg = dyn_cast_if_present<BlockArgument>(yieldedValue);
485 expressionOp.getOperand(blockArg.getArgNumber()));
494 results.
add<RemoveRecurringExpressionOperands, FoldTrivialExpressionOp>(
503 result.addAttribute(ExpressionOp::getDoNotInlineAttrName(
result.name),
508 "expected function type");
509 auto fnType = llvm::dyn_cast<FunctionType>(type);
512 "expected function type");
516 if (fnType.getNumResults() != 1)
518 "expected single return type");
519 result.addTypes(fnType.getResults());
523 bool enableNameShadowing = uniqueOperands.size() ==
result.operands.size();
525 if (enableNameShadowing) {
526 for (
auto [unresolvedOperand, operandType] :
527 llvm::zip(operands, fnType.getInputs())) {
529 argInfo.
ssaName = unresolvedOperand;
530 argInfo.
type = operandType;
531 argsInfo.push_back(argInfo);
535 if (parser.
parseRegion(*body, argsInfo, enableNameShadowing))
537 if (!enableNameShadowing) {
540 beforeRegionLoc,
"with recurring operands expected block arguments");
548 auto operands = getDefs();
553 bool printEntryBlockArgs =
true;
554 if (uniqueOperands.size() == operands.size()) {
556 printEntryBlockArgs =
false;
563 auto yieldOp = cast<YieldOp>(getBody()->getTerminator());
564 Value yieldedValue = yieldOp.getResult();
568LogicalResult ExpressionOp::verify() {
569 Type resultType = getResult().getType();
570 Region ®ion = getRegion();
575 return emitOpError(
"must yield a value at termination");
578 Value yieldResult = yield.getResult();
581 return emitOpError(
"must yield a value at termination");
586 return emitOpError(
"yielded value has no defining op");
589 return emitOpError(
"yielded value not defined within expression");
593 if (resultType != yieldType)
594 return emitOpError(
"requires yielded type to match return type");
597 auto expressionInterface = dyn_cast<emitc::CExpressionInterface>(op);
598 if (!expressionInterface)
599 return emitOpError(
"contains an unsupported operation");
600 if (op.getNumResults() != 1)
601 return emitOpError(
"requires exactly one result for each operation");
604 return emitOpError(
"contains an unused operation");
611 worklist.push_back(rootOp);
612 while (!worklist.empty()) {
615 if (visited.contains(op)) {
616 auto cExpr = cast<CExpressionInterface>(op);
617 if (!cExpr.alwaysInline() && cExpr.hasSideEffects())
619 "requires exactly one use for operations with side effects");
623 if (
Operation *def = operand.getDefiningOp()) {
624 worklist.push_back(def);
630 if (getDoNotInline() &&
631 cast<emitc::CExpressionInterface>(rootOp).alwaysInline()) {
632 return emitOpError(
"root operation must be inlined but expression is marked"
654 ForOp::ensureTerminator(*bodyRegion, builder,
result.location);
681 regionArgs.push_back(inductionVariable);
690 regionArgs.front().type = type;
701 ForOp::ensureTerminator(*body, builder,
result.location);
711 p <<
" " << getInductionVar() <<
" = " <<
getLowerBound() <<
" to "
716 p <<
" : " << t <<
' ';
723LogicalResult ForOp::verifyRegions() {
726 if (getBody()->getNumArguments() != 1)
727 return emitOpError(
"expected body to have a single block argument for the "
728 "induction variable");
732 "expected induction variable to be same type as bounds and step");
745 return emitOpError(
"requires a 'callee' symbol reference attribute");
749 <<
"' does not reference a valid function";
755FunctionType CallOp::getCalleeType() {
756 return FunctionType::get(
getContext(), getOperandTypes(), getResultTypes());
766 auto fnAttr = getSymNameAttr();
768 return emitOpError(
"requires a 'sym_name' symbol reference attribute");
772 <<
"' does not reference a valid function";
786 state.
addAttribute(getFunctionTypeAttrName(state.
name), TypeAttr::get(type));
790 if (argAttrs.empty())
792 assert(type.getNumInputs() == argAttrs.size());
794 builder, state, argAttrs, {},
795 getArgAttrsAttrName(state.
name), getResAttrsAttrName(state.
name));
806 getFunctionTypeAttrName(
result.name), buildFuncType,
807 getArgAttrsAttrName(
result.name), getResAttrsAttrName(
result.name));
812 p, *
this,
false, getFunctionTypeAttrName(),
813 getArgAttrsAttrName(), getResAttrsAttrName());
816LogicalResult FuncOp::verify() {
817 if (llvm::any_of(getArgumentTypes(), llvm::IsaPred<LValueType>)) {
818 return emitOpError(
"cannot have lvalue type as argument");
821 if (getNumResults() > 1)
822 return emitOpError(
"requires zero or exactly one result, but has ")
825 if (getNumResults() == 1 && isa<ArrayType>(getResultTypes()[0]))
835LogicalResult ReturnOp::verify() {
836 auto function = cast<FuncOp>((*this)->getParentOp());
839 if (getNumOperands() != function.getNumResults())
841 << getNumOperands() <<
" operands, but enclosing function (@"
842 << function.getName() <<
") returns " << function.getNumResults();
844 if (function.getNumResults() == 1)
845 if (getOperand().
getType() != function.getResultTypes()[0])
846 return emitError() <<
"type of the return operand ("
847 << getOperand().getType()
848 <<
") doesn't match function result type ("
849 << function.getResultTypes()[0] <<
")"
850 <<
" in function @" << function.getName();
859 bool addThenBlock,
bool addElseBlock) {
860 assert((!addElseBlock || addThenBlock) &&
861 "must not create else block w/o then block");
875 bool withElseRegion) {
885 if (withElseRegion) {
893 assert(thenBuilder &&
"the builder callback for 'then' must be present");
900 thenBuilder(builder,
result.location);
906 elseBuilder(builder,
result.location);
912 result.regions.reserve(2);
941 bool printBlockTerminators =
false;
943 p <<
" " << getCondition();
947 printBlockTerminators);
950 Region &elseRegion = getElseRegion();
951 if (!elseRegion.
empty()) {
955 printBlockTerminators);
977 Region *elseRegion = &this->getElseRegion();
978 if (elseRegion->
empty())
991 FoldAdaptor adaptor(operands, *
this);
992 auto boolAttr = dyn_cast_or_null<BoolAttr>(adaptor.getCondition());
993 if (!boolAttr || boolAttr.getValue())
994 regions.emplace_back(&getThenRegion());
997 if (!boolAttr || !boolAttr.getValue()) {
998 if (!getElseRegion().empty())
999 regions.emplace_back(&getElseRegion());
1005void IfOp::getRegionInvocationBounds(
1008 if (
auto cond = llvm::dyn_cast_or_null<BoolAttr>(operands[0])) {
1011 invocationBounds.emplace_back(0, cond.getValue() ? 1 : 0);
1012 invocationBounds.emplace_back(0, cond.getValue() ? 0 : 1);
1015 invocationBounds.assign(2, {0, 1});
1024 bool standardInclude = getIsStandardInclude();
1027 if (standardInclude)
1029 p <<
"\"" << getInclude() <<
"\"";
1030 if (standardInclude)
1045 <<
"expected trailing '>' for standard include";
1047 if (standardInclude)
1048 result.addAttribute(
"is_standard_include",
1059LogicalResult emitc::LiteralOp::verify() {
1060 if (getValue().empty())
1061 return emitOpError() <<
"value must not be empty";
1069LogicalResult MemberOp::verify() {
1070 Type operandType = getOperand().getType();
1071 Type resultType = getResult().getType();
1072 bool resultIsWritable = isa<emitc::LValueType, emitc::ArrayType>(resultType);
1082 if (isa<emitc::LValueType>(operandType) && !resultIsWritable)
1083 return emitOpError(
"lvalues must return lvalues or arrays");
1085 if (!isa<emitc::LValueType>(operandType) && resultIsWritable)
1086 return emitOpError(
"non-lvalues cannot return lvalues or arrays");
1095LogicalResult SubOp::verify() {
1096 Type lhsType = getLhs().getType();
1097 Type rhsType = getRhs().getType();
1098 Type resultType = getResult().getType();
1100 if (isa<emitc::PointerType>(rhsType) && !isa<emitc::PointerType>(lhsType))
1101 return emitOpError(
"rhs can only be a pointer if lhs is a pointer");
1103 if (isa<emitc::PointerType>(lhsType) &&
1104 !isa<IntegerType, emitc::OpaqueType, emitc::PointerType>(rhsType))
1105 return emitOpError(
"requires that rhs is an integer, pointer or of opaque "
1106 "type if lhs is a pointer");
1108 if (isa<emitc::PointerType>(lhsType) && isa<emitc::PointerType>(rhsType) &&
1109 !isa<IntegerType, emitc::PtrDiffTType, emitc::OpaqueType>(resultType))
1110 return emitOpError(
"requires that the result is an integer, ptrdiff_t or "
1111 "of opaque type if lhs and rhs are pointers");
1119LogicalResult emitc::VariableOp::verify() {
1127LogicalResult emitc::YieldOp::verify() {
1132 return emitOpError() <<
"yields a value not returned by parent";
1135 return emitOpError() <<
"does not yield a value to be returned by parent";
1137 if (
result && isa<emitc::LValueType>(
result.getType()) &&
1138 !isa<ExpressionOp>(containingOp))
1139 return emitOpError() <<
"yielding lvalues is not supported for this op";
1148LogicalResult emitc::SubscriptOp::verify() {
1150 if (
auto arrayType = llvm::dyn_cast<emitc::ArrayType>(getValue().
getType())) {
1152 if (
getIndices().size() != (
size_t)arrayType.getRank()) {
1153 return emitOpError() <<
"on array operand requires number of indices ("
1155 <<
") to match the rank of the array type ("
1156 << arrayType.getRank() <<
")";
1159 for (
unsigned i = 0, e =
getIndices().size(); i != e; ++i) {
1162 return emitOpError() <<
"on array operand requires index operand " << i
1163 <<
" to be integer-like, but got " << type;
1167 Type elementType = arrayType.getElementType();
1169 if (elementType != resultType) {
1170 return emitOpError() <<
"on array operand requires element type ("
1171 << elementType <<
") and result type (" << resultType
1178 if (
auto pointerType =
1179 llvm::dyn_cast<emitc::PointerType>(getValue().
getType())) {
1183 <<
"on pointer operand requires one index operand, but got "
1189 return emitOpError() <<
"on pointer operand requires index operand to be "
1190 "integer-like, but got "
1194 Type pointeeType = pointerType.getPointee();
1196 if (pointeeType != resultType) {
1197 return emitOpError() <<
"on pointer operand requires pointee type ("
1198 << pointeeType <<
") and result type (" << resultType
1213LogicalResult emitc::VerbatimOp::verify() {
1217 FailureOr<SmallVector<ReplacementItem>> fmt =
1222 size_t numPlaceholders = llvm::count_if(*fmt, [](
ReplacementItem &item) {
1223 return std::holds_alternative<Placeholder>(item);
1226 if (numPlaceholders != getFmtArgs().size()) {
1228 <<
"requires operands for each placeholder in the format string";
1233FailureOr<SmallVector<ReplacementItem>> emitc::VerbatimOp::parseFormatString() {
1235 return ::parseFormatString(getValue(), getFmtArgs());
1242#include "mlir/Dialect/EmitC/IR/EmitCEnums.cpp.inc"
1248#define GET_ATTRDEF_CLASSES
1249#include "mlir/Dialect/EmitC/IR/EmitCAttributes.cpp.inc"
1255#define GET_TYPEDEF_CLASSES
1256#include "mlir/Dialect/EmitC/IR/EmitCTypes.cpp.inc"
1277 if (!isValidElementType(elementType))
1278 return parser.
emitError(typeLoc,
"invalid array element type '")
1279 << elementType <<
"'",
1283 return parser.
getChecked<ArrayType>(dimensions, elementType);
1286void emitc::ArrayType::print(
AsmPrinter &printer)
const {
1289 printer << dim <<
'x';
1295LogicalResult emitc::ArrayType::verify(
1299 return emitError() <<
"shape must not be empty";
1303 return emitError() <<
"dimensions must have non-negative size";
1307 return emitError() <<
"element type must not be none";
1309 if (!isValidElementType(elementType))
1310 return emitError() <<
"invalid array element type";
1317 Type elementType)
const {
1319 return emitc::ArrayType::get(
getShape(), elementType);
1320 return emitc::ArrayType::get(*
shape, elementType);
1327LogicalResult mlir::emitc::LValueType::verify(
1334 <<
"!emitc.lvalue must wrap supported emitc type, but got " << value;
1336 if (llvm::isa<emitc::ArrayType>(value))
1337 return emitError() <<
"!emitc.lvalue cannot wrap !emitc.array type";
1346LogicalResult mlir::emitc::OpaqueType::verify(
1348 llvm::StringRef value) {
1349 if (value.empty()) {
1350 return emitError() <<
"expected non empty string in !emitc.opaque type";
1352 if (value.back() ==
'*') {
1353 return emitError() <<
"pointer not allowed as outer type with "
1354 "!emitc.opaque, use !emitc.ptr instead";
1363LogicalResult mlir::emitc::PointerType::verify(
1365 if (llvm::isa<emitc::LValueType>(value))
1366 return emitError() <<
"pointers to lvalues are not allowed";
1385 if (
auto array = llvm::dyn_cast<ArrayType>(type))
1386 return RankedTensorType::get(array.getShape(), array.getElementType());
1397 typeAttr = TypeAttr::get(type);
1405 if (!llvm::isa<ElementsAttr, IntegerAttr, FloatAttr, emitc::OpaqueAttr>(
1408 <<
"initial value should be a integer, float, elements or opaque "
1413LogicalResult GlobalOp::verify() {
1417 if (getInitialValue().has_value()) {
1418 Attribute initValue = getInitialValue().value();
1421 if (
auto elementsAttr = llvm::dyn_cast<ElementsAttr>(initValue)) {
1422 auto arrayType = llvm::dyn_cast<ArrayType>(
getType());
1426 Type initType = elementsAttr.getType();
1428 if (initType != tensorType) {
1429 return emitOpError(
"initial value expected to be of type ")
1430 <<
getType() <<
", but was of type " << initType;
1432 }
else if (
auto intAttr = dyn_cast<IntegerAttr>(initValue)) {
1433 if (intAttr.getType() !=
getType()) {
1434 return emitOpError(
"initial value expected to be of type ")
1435 <<
getType() <<
", but was of type " << intAttr.getType();
1437 }
else if (
auto floatAttr = dyn_cast<FloatAttr>(initValue)) {
1438 if (floatAttr.getType() !=
getType()) {
1439 return emitOpError(
"initial value expected to be of type ")
1440 <<
getType() <<
", but was of type " << floatAttr.getType();
1442 }
else if (!isa<emitc::OpaqueAttr>(initValue)) {
1443 return emitOpError(
"initial value should be a integer, float, elements "
1444 "or opaque attribute, but got ")
1448 if (getStaticSpecifier() && getExternSpecifier()) {
1449 return emitOpError(
"cannot have both static and extern specifiers");
1465 << getName() <<
"' does not reference a valid emitc.global";
1467 Type resultType = getResult().getType();
1468 Type globalType = global.getType();
1471 if (llvm::isa<ArrayType>(globalType)) {
1472 if (globalType != resultType)
1473 return emitOpError(
"on array type expects result type ")
1474 << resultType <<
" to match type " << globalType
1475 <<
" of the global @" << getName();
1480 auto lvalueType = dyn_cast<LValueType>(resultType);
1482 return emitOpError(
"on non-array type expects result type to be an "
1483 "lvalue type for the global @")
1485 if (lvalueType.getValueType() != globalType)
1486 return emitOpError(
"on non-array type expects result inner type ")
1487 << lvalueType.getValueType() <<
" to match type " << globalType
1488 <<
" of the global @" << getName();
1503 Region ®ion = *caseRegions.emplace_back(std::make_unique<Region>());
1507 caseValues.push_back(value);
1516 for (
auto [value, region] : llvm::zip(cases.
asArrayRef(), caseRegions)) {
1518 p <<
"case " << value <<
' ';
1524 const Twine &name) {
1525 auto yield = dyn_cast<emitc::YieldOp>(region.
front().
back());
1527 return op.emitOpError(
"expected region to end with emitc.yield, but got ")
1530 if (yield.getNumOperands() != 0) {
1531 return (op.emitOpError(
"expected each region to return ")
1532 <<
"0 values, but " << name <<
" returns "
1533 << yield.getNumOperands())
1534 .attachNote(yield.getLoc())
1535 <<
"see yield operation here";
1541LogicalResult emitc::SwitchOp::verify() {
1543 return emitOpError(
"unsupported type ") << getArg().getType();
1545 if (getCases().size() != getCaseRegions().size()) {
1547 << getCaseRegions().size() <<
" case regions but "
1548 << getCases().size() <<
" case values";
1552 for (
int64_t value : getCases())
1553 if (!valueSet.insert(value).second)
1554 return emitOpError(
"has duplicate case value: ") << value;
1559 for (
auto [idx, caseRegion] : llvm::enumerate(getCaseRegions()))
1566unsigned emitc::SwitchOp::getNumCases() {
return getCases().size(); }
1568Block &emitc::SwitchOp::getDefaultBlock() {
return getDefaultRegion().
front(); }
1570Block &emitc::SwitchOp::getCaseBlock(
unsigned idx) {
1571 assert(idx < getNumCases() &&
"case index out-of-bounds");
1572 return getCaseRegions()[idx].front();
1575void SwitchOp::getSuccessorRegions(
1577 llvm::append_range(successors, getRegions());
1583 Type type = attr.getType();
1585 return attr.getInt();
1587 return attr.getSInt();
1589 return static_cast<int64_t>(attr.getUInt());
1590 return std::nullopt;
1593void SwitchOp::getEntrySuccessorRegions(
1596 FoldAdaptor adaptor(operands, *
this);
1599 auto arg = dyn_cast_or_null<IntegerAttr>(adaptor.getArg());
1601 llvm::append_range(successors, getRegions());
1608 llvm::append_range(successors, getRegions());
1614 for (
auto [caseValue, caseRegion] : llvm::zip(getCases(), getCaseRegions())) {
1615 if (caseValue == *argValue) {
1616 successors.emplace_back(&caseRegion);
1620 successors.emplace_back(&getDefaultRegion());
1623void SwitchOp::getRegionInvocationBounds(
1625 auto operandValue = llvm::dyn_cast_or_null<IntegerAttr>(operands.front());
1626 if (!operandValue) {
1633 if (!maybeIntValue) {
1639 unsigned liveIndex = getNumRegions() - 1;
1640 const auto *iteratorToInt = llvm::find(getCases(), *maybeIntValue);
1642 liveIndex = iteratorToInt != getCases().end()
1643 ? std::distance(getCases().begin(), iteratorToInt)
1646 for (
unsigned regIndex = 0, regNum = getNumRegions(); regIndex < regNum;
1648 bounds.emplace_back(0, regIndex == liveIndex);
1675 if (
auto array = llvm::dyn_cast<ArrayType>(type))
1676 return RankedTensorType::get(array.getShape(), array.getElementType());
1687 typeAttr = TypeAttr::get(type);
1695 if (!llvm::isa<ElementsAttr, IntegerAttr, FloatAttr, emitc::OpaqueAttr>(
1698 <<
"initial value should be a integer, float, elements or opaque "
1703LogicalResult FieldOp::verify() {
1708 if (!parentOp || !isa<emitc::ClassOp>(parentOp))
1709 return emitOpError(
"field must be nested within an emitc.class operation");
1711 StringAttr symName = getSymNameAttr();
1712 if (!symName || symName.getValue().empty())
1713 return emitOpError(
"field must have a non-empty symbol name");
1728 << fieldNameAttr <<
"' not found in the class";
1730 Type getFieldResultType = getResult().getType();
1731 Type fieldType = fieldOp.getType();
1733 if (fieldType != getFieldResultType)
1735 << getFieldResultType <<
" does not match field '" << fieldNameAttr
1736 <<
"' type " << fieldType;
1753LogicalResult emitc::DoOp::verify() {
1754 Block &condBlock = getConditionRegion().
front();
1758 "condition region must contain exactly two operations: "
1759 "'emitc.expression' followed by 'emitc.yield', but found ")
1763 auto exprOp = dyn_cast<emitc::ExpressionOp>(first);
1765 return emitOpError(
"expected first op in condition region to be "
1766 "'emitc.expression', but got ")
1769 if (!exprOp.getResult().getType().isInteger(1))
1770 return emitOpError(
"emitc.expression in condition region must return "
1771 "'i1', but returns ")
1772 << exprOp.getResult().getType();
1775 auto condYield = dyn_cast<emitc::YieldOp>(last);
1777 return emitOpError(
"expected last op in condition region to be "
1778 "'emitc.yield', but got ")
1781 if (condYield.getNumOperands() != 1)
1782 return emitOpError(
"expected condition region to return 1 value, but "
1784 << condYield.getNumOperands() <<
" values";
1786 if (condYield.getOperand(0) != exprOp.getResult())
1787 return emitError(
"'emitc.yield' must return result of "
1788 "'emitc.expression' from this condition region");
1792 return emitOpError(
"body region must not contain terminator");
1805 if (bodyRegion->
empty())
1815#include "mlir/Dialect/EmitC/IR/EmitCInterfaces.cpp.inc"
1817#define GET_OP_CLASSES
1818#include "mlir/Dialect/EmitC/IR/EmitC.cpp.inc"
p<< " : "<< getMemRefType()<< ", "<< getType();}static LogicalResult verifyVectorMemoryOp(Operation *op, MemRefType memrefType, VectorType vectorType) { if(memrefType.getElementType() !=vectorType.getElementType()) return op-> emitOpError("requires memref and vector types of the same elemental type")
Given a list of lists of parsed operands, populates uniqueOperands with unique operands.
static std::optional< int64_t > getUpperBound(Value iv)
Gets the constant upper bound on an affine.for iv.
static std::optional< int64_t > getLowerBound(Value iv)
Gets the constant lower bound on an iv.
static std::optional< int64_t > getIntAttrValue(IntegerAttr attr)
Returns the int64_t value of an IntegerAttr regardless of whether its type is signless,...
static LogicalResult verifyInitializationAttribute(Operation *op, Attribute value)
Check that the type of the initial value is compatible with the operations result type.
static LogicalResult verifyRegion(emitc::SwitchOp op, Region ®ion, const Twine &name)
static ParseResult parseEmitCGlobalOpTypeAndInitialValue(OpAsmParser &parser, TypeAttr &typeAttr, Attribute &initialValue)
static Type getInitializerTypeForField(Type type)
static ParseResult parseEmitCFieldOpTypeAndInitialValue(OpAsmParser &parser, TypeAttr &typeAttr, Attribute &initialValue)
FailureOr< SmallVector< ReplacementItem > > parseFormatString(StringRef toParse, ArgType fmtArgs, llvm::function_ref< mlir::InFlightDiagnostic()> emitError={})
Parse a format string and return a list of its parts.
static void printEmitCGlobalOpTypeAndInitialValue(OpAsmPrinter &p, GlobalOp op, TypeAttr type, Attribute initialValue)
static LogicalResult verifyAssignmentOp(AssignmentOp op)
static ParseResult parseSwitchCases(OpAsmParser &parser, DenseI64ArrayAttr &cases, SmallVectorImpl< std::unique_ptr< Region > > &caseRegions)
Parse the case regions and values.
static LogicalResult verifyOpaqueCallCommon(Operation *op, StringRef callee, std::optional< ArrayAttr > args, std::optional< ArrayAttr > templateArgs, TypeRange resultTypes, size_t numArgsOperands)
static void printEmitCFieldOpTypeAndInitialValue(OpAsmPrinter &p, FieldOp op, TypeAttr type, Attribute initialValue)
static void printSwitchCases(OpAsmPrinter &p, Operation *op, DenseI64ArrayAttr cases, RegionRange caseRegions)
Print the case regions and values.
static Type getInitializerTypeForGlobal(Type type)
static Type getElementType(Type type)
Determine the element type of type.
static Type getValueType(Attribute attr)
static ArrayRef< int64_t > getShape(Type type)
Returns the shape of the given type.
This base class exposes generic asm parser hooks, usable across the various derived parsers.
virtual Builder & getBuilder() const =0
Return a builder which provides useful access to MLIRContext, global objects like types and attribute...
virtual ParseResult parseOptionalAttrDict(NamedAttrList &result)=0
Parse a named dictionary into 'result' if it is present.
virtual ParseResult parseOptionalEqual()=0
Parse a = token if present.
virtual ParseResult parseOptionalKeyword(StringRef keyword)=0
Parse the given keyword if present.
MLIRContext * getContext() const
virtual InFlightDiagnostic emitError(SMLoc loc, const Twine &message={})=0
Emit a diagnostic at the specified location and return failure.
virtual ParseResult parseOptionalColon()=0
Parse a : token if present.
ParseResult parseInteger(IntT &result)
Parse an integer value from the stream.
virtual ParseResult parseLess()=0
Parse a '<' token.
virtual ParseResult parseDimensionList(SmallVectorImpl< int64_t > &dimensions, bool allowDynamic=true, bool withTrailingX=true)=0
Parse a dimension list of a tensor or memref type.
virtual ParseResult parseOptionalGreater()=0
Parse a '>' token if present.
virtual ParseResult parseEqual()=0
Parse a = token.
virtual ParseResult parseOptionalAttrDictWithKeyword(NamedAttrList &result)=0
Parse a named dictionary into 'result' if the attributes keyword is present.
virtual ParseResult parseColonType(Type &result)=0
Parse a colon followed by a type.
virtual OptionalParseResult parseOptionalAttribute(Attribute &result, Type type={})=0
Parse an arbitrary optional attribute of a given type and return it in result.
virtual SMLoc getCurrentLocation()=0
Get the location of the next token and store it into the argument.
auto getChecked(SMLoc loc, ParamsT &&...params)
Invoke the getChecked method of the given Attribute or Type class, using the provided location to emi...
virtual SMLoc getNameLoc() const =0
Return the location of the original name token.
virtual ParseResult parseOptionalLess()=0
Parse a '<' token if present.
virtual ParseResult parseGreater()=0
Parse a '>' token.
virtual ParseResult parseType(Type &result)=0
Parse a type.
ParseResult parseKeyword(StringRef keyword)
Parse a given keyword.
virtual ParseResult parseAttribute(Attribute &result, Type type={})=0
Parse an arbitrary attribute of a given type and return it in result.
This base class exposes generic asm printer hooks, usable across the various derived printers.
virtual void printAttributeWithoutType(Attribute attr)
Print the given attribute without its type.
virtual void printType(Type type)
virtual void printNewline()
Print a newline and indent the printer to the start of the current operation/attribute/type.
Attributes are known-constant values of operations.
Block represents an ordered list of Operations.
BlockArgument getArgument(unsigned i)
OpListType & getOperations()
Operation * getTerminator()
Get the terminator operation of this block.
BlockArgument addArgument(Type type, Location loc)
Add one value to the argument list.
bool mightHaveTerminator()
Return "true" if this block might have a terminator.
BlockArgListType getArguments()
iterator_range< iterator > without_terminator()
Return an iterator range over the operation within this block excluding the terminator operation at t...
This class is a general helper class for creating context-global objects like types,...
DenseI64ArrayAttr getDenseI64ArrayAttr(ArrayRef< int64_t > values)
FunctionType getFunctionType(TypeRange inputs, TypeRange results)
IntegerType getIntegerType(unsigned width)
StringAttr getStringAttr(const Twine &bytes)
NamedAttribute getNamedAttr(StringRef name, Attribute val)
A symbol reference with a reference path containing a single element.
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
This class represents a diagnostic that is inflight and set to be reported.
This class represents upper and lower bounds on the number of times a region of a RegionBranchOpInter...
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext is the top-level object for a collection of MLIR operations.
void push_back(NamedAttribute newAttribute)
Add an attribute with the specified name.
void append(StringRef name, Attribute attr)
Add an attribute with the specified name.
The OpAsmParser has methods for interacting with the asm parser: parsing things from it,...
virtual ParseResult parseRegion(Region ®ion, ArrayRef< Argument > arguments={}, bool enableNameShadowing=false)=0
Parses a region.
virtual ParseResult resolveOperand(const UnresolvedOperand &operand, Type type, SmallVectorImpl< Value > &result)=0
Resolve an operand to an SSA value, emitting an error on failure.
ParseResult resolveOperands(Operands &&operands, Type type, SmallVectorImpl< Value > &result)
Resolve a list of operands to SSA values, emitting an error on failure, or appending the results to t...
virtual ParseResult parseOperand(UnresolvedOperand &result, bool allowResultNumber=true)=0
Parse a single SSA value operand name along with a result number if allowResultNumber is true.
virtual ParseResult parseOperandList(SmallVectorImpl< UnresolvedOperand > &result, Delimiter delimiter=Delimiter::None, bool allowResultNumber=true, int requiredOperandCount=-1)=0
Parse zero or more SSA comma-separated operand references with a specified surrounding delimiter,...
This is a pure-virtual base class that exposes the asmprinter hooks necessary to implement a custom p...
virtual void shadowRegionArgs(Region ®ion, ValueRange namesToUse)=0
Renumber the arguments for the specified region to the same names as the SSA values in namesToUse.
void printOperands(const ContainerType &container)
Print a comma separated list of operands.
virtual void printOptionalAttrDictWithKeyword(ArrayRef< NamedAttribute > attrs, ArrayRef< StringRef > elidedAttrs={})=0
If the specified operation has attributes, print out an attribute dictionary prefixed with 'attribute...
virtual void printOptionalAttrDict(ArrayRef< NamedAttribute > attrs, ArrayRef< StringRef > elidedAttrs={})=0
If the specified operation has attributes, print out an attribute dictionary with their values.
void printFunctionalType(Operation *op)
Print the complete type of an operation in functional form.
virtual void printRegion(Region &blocks, bool printEntryBlockArgs=true, bool printBlockTerminators=true, bool printEmptyBlock=false)=0
Prints a region.
RAII guard to reset the insertion point of the builder when destroyed.
This class helps build Operations.
Block * createBlock(Region *parent, Region::iterator insertPt={}, TypeRange argTypes={}, ArrayRef< Location > locs={})
Add new block with 'argTypes' arguments and set the insertion point to the end of it.
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
This class represents a single result from folding an operation.
type_range getType() const
Operation is the basic unit of execution within MLIR.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
OperationName getName()
The name of an operation is the key identifier for it.
operand_range getOperands()
Returns an iterator on the underlying Value's.
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
unsigned getNumResults()
Return the number of results held by this operation.
This class implements Optional functionality for ParseResult.
bool has_value() const
Returns true if we contain a valid ParseResult value.
This class represents a point being branched from in the methods of the RegionBranchOpInterface.
bool isParent() const
Returns true if branching from the parent op.
This class provides an abstraction over the different types of ranges over Regions.
This class represents a successor of a region.
bool isOperation() const
Return true if the successor is an operation.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
static DerivedEffect * get()
This class represents a collection of SymbolTables.
virtual Operation * lookupNearestSymbolFrom(Operation *from, StringAttr symbol)
Returns the operation registered with the given symbol name within the closest parent operation of,...
static StringRef getSymbolAttrName()
Return the name of the attribute used for symbol names.
This class provides an abstraction over the various different ranges of value types.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
bool isSignedInteger() const
Return true if this is a signed integer type (with the specified width).
bool isSignlessInteger() const
Return true if this is a signless integer type (with the specified width).
bool isUnsignedInteger() const
Return true if this is an unsigned integer type (with the specified width).
This class provides an abstraction over the different types of ranges over Values.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
ArrayRef< T > asArrayRef() const
A named class for passing around the variadic flag.
Speculatability
This enum is returned from the getSpeculatability method in the ConditionallySpeculatable op interfac...
constexpr auto Speculatable
constexpr auto NotSpeculatable
LogicalResult verifyCallOpInterface(CallOpInterface call, TypeRange argumentTypes, TypeRange resultTypes)
Verify that the forwarded operands and results of call are in a 1:1 relationship with the given argum...
void addArgAndResultAttrs(Builder &builder, OperationState &result, ArrayRef< DictionaryAttr > argAttrs, ArrayRef< DictionaryAttr > resultAttrs, StringAttr argAttrsName, StringAttr resAttrsName)
Adds argument and result attributes, provided as argAttrs and resultAttrs arguments,...
void buildTerminatedBody(OpBuilder &builder, Location loc)
Default callback for builders of ops carrying a region.
std::variant< StringRef, Placeholder > ReplacementItem
bool isFundamentalType(mlir::Type type)
Determines whether type is a valid fundamental C++ type in EmitC.
bool isSupportedFloatType(mlir::Type type)
Determines whether type is a valid floating-point type in EmitC.
bool isSupportedEmitCType(mlir::Type type)
Determines whether type is valid in EmitC.
bool isPointerWideType(mlir::Type type)
Determines whether type is a emitc.size_t/ssize_t type.
bool isIntegerIndexOrOpaqueType(Type type)
Determines whether type is integer like, i.e.
bool isSupportedIntegerType(mlir::Type type)
Determines whether type is a valid integer type in EmitC.
void printFunctionOp(OpAsmPrinter &p, FunctionOpInterface op, bool isVariadic, StringRef typeAttrName, StringAttr argAttrsName, StringAttr resAttrsName)
Printer implementation for function-like operations.
ParseResult parseFunctionOp(OpAsmParser &parser, OperationState &result, bool allowVariadic, StringAttr typeAttrName, FuncTypeBuilder funcTypeBuilder, StringAttr argAttrsName, StringAttr resAttrsName)
Parser implementation for function-like operations.
Operation::operand_range getIndices(Operation *op)
Get the indices that the given load/store operation is operating on.
Include the generated interface declarations.
detail::DenseArrayAttrImpl< int64_t > DenseI64ArrayAttr
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
llvm::DenseSet< ValueT, ValueInfoT > DenseSet
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
llvm::SetVector< T, Vector, Set, N > SetVector
std::conditional_t< std::is_same_v< Ty, mlir::Type >, mlir::Value, detail::TypedValue< Ty > > TypedValue
If Ty is mlir::Type this will select Value instead of having a wrapper around it.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
llvm::function_ref< Fn > function_ref
UnresolvedOperand ssaName
This is the representation of an operand reference.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
This represents an operation in an abstracted form, suitable for use with the builder APIs.
void addAttribute(StringRef name, Attribute attr)
Add an attribute with the specified name.
Region * addRegion()
Create a region that should be attached to the operation.