MLIR 23.0.0git
EmitC.cpp
Go to the documentation of this file.
1//===- EmitC.cpp - EmitC Dialect ------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
11#include "mlir/IR/Builders.h"
15#include "mlir/IR/Types.h"
17#include "mlir/Support/LLVM.h"
18#include "llvm/ADT/STLExtras.h"
19#include "llvm/ADT/SmallVector.h"
20#include "llvm/ADT/TypeSwitch.h"
21#include "llvm/Support/Casting.h"
22
23using namespace mlir;
24using namespace mlir::emitc;
25
26#include "mlir/Dialect/EmitC/IR/EmitCDialect.cpp.inc"
27
28//===----------------------------------------------------------------------===//
29// EmitCDialect
30//===----------------------------------------------------------------------===//
31
32void EmitCDialect::initialize() {
33 addOperations<
34#define GET_OP_LIST
35#include "mlir/Dialect/EmitC/IR/EmitC.cpp.inc"
36 >();
37 addTypes<
38#define GET_TYPEDEF_LIST
39#include "mlir/Dialect/EmitC/IR/EmitCTypes.cpp.inc"
40 >();
41 addAttributes<
42#define GET_ATTRDEF_LIST
43#include "mlir/Dialect/EmitC/IR/EmitCAttributes.cpp.inc"
44 >();
45}
46
47/// Materialize a single constant operation from a given attribute value with
48/// the desired resultant type.
49Operation *EmitCDialect::materializeConstant(OpBuilder &builder,
50 Attribute value, Type type,
51 Location loc) {
52 return emitc::ConstantOp::create(builder, loc, type, value);
53}
54
55/// Default callback for builders of ops carrying a region. Inserts a yield
56/// without arguments.
58 emitc::YieldOp::create(builder, loc);
59}
60
62 if (llvm::isa<emitc::OpaqueType>(type))
63 return true;
64 if (auto ptrType = llvm::dyn_cast<emitc::PointerType>(type))
65 return isSupportedEmitCType(ptrType.getPointee());
66 if (auto arrayType = llvm::dyn_cast<emitc::ArrayType>(type)) {
67 auto elemType = arrayType.getElementType();
68 return !llvm::isa<emitc::ArrayType>(elemType) &&
69 isSupportedEmitCType(elemType);
70 }
71 if (type.isIndex() || emitc::isPointerWideType(type))
72 return true;
73 if (llvm::isa<IntegerType>(type))
74 return isSupportedIntegerType(type);
75 if (llvm::isa<FloatType>(type))
76 return isSupportedFloatType(type);
77 if (auto tensorType = llvm::dyn_cast<TensorType>(type)) {
78 if (!tensorType.hasStaticShape()) {
79 return false;
80 }
81 auto elemType = tensorType.getElementType();
82 if (llvm::isa<emitc::ArrayType>(elemType)) {
83 return false;
84 }
85 return isSupportedEmitCType(elemType);
86 }
87 if (auto tupleType = llvm::dyn_cast<TupleType>(type)) {
88 return llvm::all_of(tupleType.getTypes(), [](Type type) {
89 return !llvm::isa<emitc::ArrayType>(type) && isSupportedEmitCType(type);
90 });
91 }
92 return false;
93}
94
96 if (auto intType = llvm::dyn_cast<IntegerType>(type)) {
97 switch (intType.getWidth()) {
98 case 1:
99 case 8:
100 case 16:
101 case 32:
102 case 64:
103 return true;
104 default:
105 return false;
106 }
107 }
108 return false;
109}
110
112 return llvm::isa<IndexType, emitc::OpaqueType>(type) ||
114}
115
117 if (auto floatType = llvm::dyn_cast<FloatType>(type)) {
118 switch (floatType.getWidth()) {
119 case 16:
120 return llvm::isa<Float16Type, BFloat16Type>(type);
121 case 32:
122 case 64:
123 return true;
124 default:
125 return false;
126 }
127 }
128 return false;
129}
130
132 return isa<emitc::SignedSizeTType, emitc::SizeTType, emitc::PtrDiffTType>(
133 type);
134}
135
137 return llvm::isa<IndexType>(type) || isPointerWideType(type) ||
139 isa<emitc::PointerType>(type);
140}
141
142/// Check that the type of the initial value is compatible with the operations
143/// result type.
145 Attribute value) {
146 assert(op->getNumResults() == 1 && "operation must have 1 result");
147
148 if (llvm::isa<emitc::OpaqueAttr>(value))
149 return success();
150
151 if (llvm::isa<StringAttr>(value))
152 return op->emitOpError()
153 << "string attributes are not supported, use #emitc.opaque instead";
154
155 Type resultType = op->getResult(0).getType();
156 if (auto lType = dyn_cast<LValueType>(resultType))
157 resultType = lType.getValueType();
158 Type attrType = cast<TypedAttr>(value).getType();
159
160 if (isPointerWideType(resultType) && attrType.isIndex())
161 return success();
162
163 if (resultType != attrType)
164 return op->emitOpError()
165 << "requires attribute to either be an #emitc.opaque attribute or "
166 "it's type ("
167 << attrType << ") to match the op's result type (" << resultType
168 << ")";
169
170 return success();
171}
172
173/// Parse a format string and return a list of its parts.
174/// A part is either a StringRef that has to be printed as-is, or
175/// a Placeholder which requires printing the next operand of the VerbatimOp.
176/// In the format string, all `{}` are replaced by Placeholders, except if the
177/// `{` is escaped by `{{` - then it doesn't start a placeholder.
178template <class ArgType>
179FailureOr<SmallVector<ReplacementItem>> parseFormatString(
180 StringRef toParse, ArgType fmtArgs,
183
184 // If there are not operands, the format string is not interpreted.
185 if (fmtArgs.empty()) {
186 items.push_back(toParse);
187 return items;
188 }
189
190 while (!toParse.empty()) {
191 size_t idx = toParse.find('{');
192 if (idx == StringRef::npos) {
193 // No '{'
194 items.push_back(toParse);
195 break;
196 }
197 if (idx > 0) {
198 // Take all chars excluding the '{'.
199 items.push_back(toParse.take_front(idx));
200 toParse = toParse.drop_front(idx);
201 continue;
202 }
203 if (toParse.size() < 2) {
204 return emitError() << "expected '}' after unescaped '{' at end of string";
205 }
206 // toParse contains at least two characters and starts with `{`.
207 char nextChar = toParse[1];
208 if (nextChar == '{') {
209 // Double '{{' -> '{' (escaping).
210 items.push_back(toParse.take_front(1));
211 toParse = toParse.drop_front(2);
212 continue;
213 }
214 if (nextChar == '}') {
215 items.push_back(Placeholder{});
216 toParse = toParse.drop_front(2);
217 continue;
218 }
219
220 if (emitError) {
221 return emitError() << "expected '}' after unescaped '{'";
222 }
223 return failure();
224 }
225 return items;
226}
227
228//===----------------------------------------------------------------------===//
229// AddressOfOp
230//===----------------------------------------------------------------------===//
231
232LogicalResult AddressOfOp::verify() {
233 emitc::LValueType referenceType = getReference().getType();
234 emitc::PointerType resultType = getResult().getType();
235
236 if (referenceType.getValueType() != resultType.getPointee())
237 return emitOpError("requires result to be a pointer to the type "
238 "referenced by operand");
239
240 return success();
241}
242
243//===----------------------------------------------------------------------===//
244// AddOp
245//===----------------------------------------------------------------------===//
246
247LogicalResult AddOp::verify() {
248 Type lhsType = getLhs().getType();
249 Type rhsType = getRhs().getType();
250
251 if (isa<emitc::PointerType>(lhsType) && isa<emitc::PointerType>(rhsType))
252 return emitOpError("requires that at most one operand is a pointer");
253
254 if ((isa<emitc::PointerType>(lhsType) &&
255 !isa<IntegerType, emitc::OpaqueType>(rhsType)) ||
256 (isa<emitc::PointerType>(rhsType) &&
257 !isa<IntegerType, emitc::OpaqueType>(lhsType)))
258 return emitOpError("requires that one operand is an integer or of opaque "
259 "type if the other is a pointer");
260
261 return success();
262}
263
264//===----------------------------------------------------------------------===//
265// ApplyOp
266//===----------------------------------------------------------------------===//
267
268LogicalResult ApplyOp::verify() {
269 StringRef applicableOperatorStr = getApplicableOperator();
270
271 // Applicable operator must not be empty.
272 if (applicableOperatorStr.empty())
273 return emitOpError("applicable operator must not be empty");
274
275 // Only `*` and `&` are supported.
276 if (applicableOperatorStr != "&" && applicableOperatorStr != "*")
277 return emitOpError("applicable operator is illegal");
278
279 Type operandType = getOperand().getType();
280 Type resultType = getResult().getType();
281 if (applicableOperatorStr == "&") {
282 if (!llvm::isa<emitc::LValueType>(operandType))
283 return emitOpError("operand type must be an lvalue when applying `&`");
284 if (!llvm::isa<emitc::PointerType>(resultType))
285 return emitOpError("result type must be a pointer when applying `&`");
286 } else {
287 if (!llvm::isa<emitc::PointerType>(operandType))
288 return emitOpError("operand type must be a pointer when applying `*`");
289 }
290
291 return success();
292}
293
294//===----------------------------------------------------------------------===//
295// AssignOp
296//===----------------------------------------------------------------------===//
297
298/// The assign op requires that the assigned value's type matches the
299/// assigned-to variable type.
300LogicalResult emitc::AssignOp::verify() {
302
303 if (!variable.getDefiningOp())
304 return emitOpError() << "cannot assign to block argument";
305
306 Type valueType = getValue().getType();
307 Type variableType = variable.getType().getValueType();
308 if (variableType != valueType)
309 return emitOpError() << "requires value's type (" << valueType
310 << ") to match variable's type (" << variableType
311 << ")\n variable: " << variable
312 << "\n value: " << getValue() << "\n";
313 return success();
314}
315
316//===----------------------------------------------------------------------===//
317// CastOp
318//===----------------------------------------------------------------------===//
319
320bool CastOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
321 Type input = inputs.front(), output = outputs.front();
322
323 if (auto arrayType = dyn_cast<emitc::ArrayType>(input)) {
324 if (auto pointerType = dyn_cast<emitc::PointerType>(output)) {
325 return (arrayType.getElementType() == pointerType.getPointee()) &&
326 arrayType.getShape().size() == 1 && arrayType.getShape()[0] >= 1;
327 }
328 return false;
329 }
330
331 return (
333 emitc::isSupportedFloatType(input) || isa<emitc::PointerType>(input)) &&
335 emitc::isSupportedFloatType(output) || isa<emitc::PointerType>(output)));
336}
337
338//===----------------------------------------------------------------------===//
339// CallOpaqueOp
340//===----------------------------------------------------------------------===//
341
342LogicalResult emitc::CallOpaqueOp::verify() {
343 // Callee must not be empty.
344 if (getCallee().empty())
345 return emitOpError("callee must not be empty");
346
347 if (std::optional<ArrayAttr> argsAttr = getArgs()) {
348 for (Attribute arg : *argsAttr) {
349 auto intAttr = llvm::dyn_cast<IntegerAttr>(arg);
350 if (intAttr && llvm::isa<IndexType>(intAttr.getType())) {
351 int64_t index = intAttr.getInt();
352 // Args with elements of type index must be in range
353 // [0..operands.size).
354 if ((index < 0) || (index >= static_cast<int64_t>(getNumOperands())))
355 return emitOpError("index argument is out of range");
356
357 // Args with elements of type ArrayAttr must have a type.
358 } else if (llvm::isa<ArrayAttr>(
359 arg) /*&& llvm::isa<NoneType>(arg.getType())*/) {
360 // FIXME: Array attributes never have types
361 return emitOpError("array argument has no type");
362 }
363 }
364 }
365
366 if (std::optional<ArrayAttr> templateArgsAttr = getTemplateArgs()) {
367 for (Attribute tArg : *templateArgsAttr) {
368 if (!llvm::isa<TypeAttr, IntegerAttr, FloatAttr, emitc::OpaqueAttr>(tArg))
369 return emitOpError("template argument has invalid type");
370 }
371 }
372
373 if (llvm::any_of(getResultTypes(), llvm::IsaPred<ArrayType>)) {
374 return emitOpError() << "cannot return array type";
375 }
376
377 return success();
378}
379
380//===----------------------------------------------------------------------===//
381// ConstantOp
382//===----------------------------------------------------------------------===//
383
384LogicalResult emitc::ConstantOp::verify() {
385 Attribute value = getValueAttr();
386 if (failed(verifyInitializationAttribute(getOperation(), value)))
387 return failure();
388 if (auto opaqueValue = llvm::dyn_cast<emitc::OpaqueAttr>(value)) {
389 if (opaqueValue.getValue().empty())
390 return emitOpError() << "value must not be empty";
391 }
392 return success();
393}
394
395OpFoldResult emitc::ConstantOp::fold(FoldAdaptor adaptor) { return getValue(); }
396
397//===----------------------------------------------------------------------===//
398// DereferenceOp
399//===----------------------------------------------------------------------===//
400
401LogicalResult DereferenceOp::verify() {
402 emitc::PointerType pointerType = getPointer().getType();
403
404 if (pointerType.getPointee() != getResult().getType().getValueType())
405 return emitOpError("requires result to be an lvalue of the type "
406 "pointed to by operand");
407
408 return success();
409}
410
411//===----------------------------------------------------------------------===//
412// ExpressionOp
413//===----------------------------------------------------------------------===//
414
415ParseResult ExpressionOp::parse(OpAsmParser &parser, OperationState &result) {
417 if (parser.parseOperandList(operands))
418 return parser.emitError(parser.getCurrentLocation()) << "expected operands";
419 if (succeeded(parser.parseOptionalKeyword("noinline")))
420 result.addAttribute(ExpressionOp::getDoNotInlineAttrName(result.name),
421 parser.getBuilder().getUnitAttr());
422 Type type;
423 if (parser.parseColonType(type))
424 return parser.emitError(parser.getCurrentLocation(),
425 "expected function type");
426 auto fnType = llvm::dyn_cast<FunctionType>(type);
427 if (!fnType)
428 return parser.emitError(parser.getCurrentLocation(),
429 "expected function type");
430 if (parser.resolveOperands(operands, fnType.getInputs(),
431 parser.getCurrentLocation(), result.operands))
432 return failure();
433 if (fnType.getNumResults() != 1)
434 return parser.emitError(parser.getCurrentLocation(),
435 "expected single return type");
436 result.addTypes(fnType.getResults());
437 Region *body = result.addRegion();
439 for (auto [unresolvedOperand, operandType] :
440 llvm::zip(operands, fnType.getInputs())) {
441 OpAsmParser::Argument argInfo;
442 argInfo.ssaName = unresolvedOperand;
443 argInfo.type = operandType;
444 argsInfo.push_back(argInfo);
445 }
446 if (parser.parseRegion(*body, argsInfo, /*enableNameShadowing=*/true))
447 return failure();
448 return success();
449}
450
451void emitc::ExpressionOp::print(OpAsmPrinter &p) {
452 p << ' ';
453 p.printOperands(getDefs());
454 p << " : ";
455 p.printFunctionalType(getOperation());
456 p.shadowRegionArgs(getRegion(), getDefs());
457 p << ' ';
458 p.printRegion(getRegion(), /*printEntryBlockArgs=*/false);
459}
460
461Operation *ExpressionOp::getRootOp() {
462 auto yieldOp = cast<YieldOp>(getBody()->getTerminator());
463 Value yieldedValue = yieldOp.getResult();
464 return yieldedValue.getDefiningOp();
465}
466
467LogicalResult ExpressionOp::verify() {
468 Type resultType = getResult().getType();
469 Region &region = getRegion();
470
471 Block &body = region.front();
472
473 if (!body.mightHaveTerminator())
474 return emitOpError("must yield a value at termination");
475
476 auto yield = cast<YieldOp>(body.getTerminator());
477 Value yieldResult = yield.getResult();
478
479 if (!yieldResult)
480 return emitOpError("must yield a value at termination");
481
482 Operation *rootOp = yieldResult.getDefiningOp();
483
484 if (!rootOp)
485 return emitOpError("yielded value has no defining op");
486
487 if (rootOp->getParentOp() != getOperation())
488 return emitOpError("yielded value not defined within expression");
489
490 Type yieldType = yieldResult.getType();
491
492 if (resultType != yieldType)
493 return emitOpError("requires yielded type to match return type");
494
495 for (Operation &op : region.front().without_terminator()) {
496 auto expressionInterface = dyn_cast<emitc::CExpressionInterface>(op);
497 if (!expressionInterface)
498 return emitOpError("contains an unsupported operation");
499 if (op.getNumResults() != 1)
500 return emitOpError("requires exactly one result for each operation");
501 Value result = op.getResult(0);
502 if (result.use_empty())
503 return emitOpError("contains an unused operation");
504 }
505
506 // Make sure any operation with side effect is only reachable once from
507 // the root op, otherwise emission will be replicating side effects.
510 worklist.push_back(rootOp);
511 while (!worklist.empty()) {
512 Operation *op = worklist.back();
513 worklist.pop_back();
514 if (visited.contains(op)) {
515 if (cast<CExpressionInterface>(op).hasSideEffects())
516 return emitOpError(
517 "requires exactly one use for operations with side effects");
518 }
519 visited.insert(op);
520 for (Value operand : op->getOperands())
521 if (Operation *def = operand.getDefiningOp()) {
522 worklist.push_back(def);
523 }
524 }
525
526 return success();
527}
528
529//===----------------------------------------------------------------------===//
530// ForOp
531//===----------------------------------------------------------------------===//
532
533void ForOp::build(OpBuilder &builder, OperationState &result, Value lb,
534 Value ub, Value step, BodyBuilderFn bodyBuilder) {
535 OpBuilder::InsertionGuard g(builder);
536 result.addOperands({lb, ub, step});
537 Type t = lb.getType();
538 Region *bodyRegion = result.addRegion();
539 Block *bodyBlock = builder.createBlock(bodyRegion);
540 bodyBlock->addArgument(t, result.location);
541
542 // Create the default terminator if the builder is not provided.
543 if (!bodyBuilder) {
544 ForOp::ensureTerminator(*bodyRegion, builder, result.location);
545 } else {
546 OpBuilder::InsertionGuard guard(builder);
547 builder.setInsertionPointToStart(bodyBlock);
548 bodyBuilder(builder, result.location, bodyBlock->getArgument(0));
549 }
550}
551
552void ForOp::getCanonicalizationPatterns(RewritePatternSet &, MLIRContext *) {}
553
554ParseResult ForOp::parse(OpAsmParser &parser, OperationState &result) {
555 Builder &builder = parser.getBuilder();
556 Type type;
557
558 OpAsmParser::Argument inductionVariable;
560
561 // Parse the induction variable followed by '='.
562 if (parser.parseOperand(inductionVariable.ssaName) || parser.parseEqual() ||
563 // Parse loop bounds.
564 parser.parseOperand(lb) || parser.parseKeyword("to") ||
565 parser.parseOperand(ub) || parser.parseKeyword("step") ||
566 parser.parseOperand(step))
567 return failure();
568
569 // Parse the optional initial iteration arguments.
571 regionArgs.push_back(inductionVariable);
572
573 // Parse optional type, else assume Index.
574 if (parser.parseOptionalColon())
575 type = builder.getIndexType();
576 else if (parser.parseType(type))
577 return failure();
578
579 // Resolve input operands.
580 regionArgs.front().type = type;
581 if (parser.resolveOperand(lb, type, result.operands) ||
582 parser.resolveOperand(ub, type, result.operands) ||
583 parser.resolveOperand(step, type, result.operands))
584 return failure();
585
586 // Parse the body region.
587 Region *body = result.addRegion();
588 if (parser.parseRegion(*body, regionArgs))
589 return failure();
590
591 ForOp::ensureTerminator(*body, builder, result.location);
592
593 // Parse the optional attribute list.
594 if (parser.parseOptionalAttrDict(result.attributes))
595 return failure();
596
597 return success();
598}
599
600void ForOp::print(OpAsmPrinter &p) {
601 p << " " << getInductionVar() << " = " << getLowerBound() << " to "
602 << getUpperBound() << " step " << getStep();
603
604 p << ' ';
605 if (Type t = getInductionVar().getType(); !t.isIndex())
606 p << " : " << t << ' ';
607 p.printRegion(getRegion(),
608 /*printEntryBlockArgs=*/false,
609 /*printBlockTerminators=*/false);
610 p.printOptionalAttrDict((*this)->getAttrs());
611}
612
613LogicalResult ForOp::verifyRegions() {
614 // Check that the body defines as single block argument for the induction
615 // variable.
616 if (getBody()->getNumArguments() != 1)
617 return emitOpError("expected body to have a single block argument for the "
618 "induction variable");
619
620 if (getInductionVar().getType() != getLowerBound().getType())
621 return emitOpError(
622 "expected induction variable to be same type as bounds and step");
623
624 return success();
625}
626
627//===----------------------------------------------------------------------===//
628// CallOp
629//===----------------------------------------------------------------------===//
630
631LogicalResult CallOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
632 // Check that the callee attribute was specified.
633 auto fnAttr = (*this)->getAttrOfType<FlatSymbolRefAttr>("callee");
634 if (!fnAttr)
635 return emitOpError("requires a 'callee' symbol reference attribute");
636 FuncOp fn = symbolTable.lookupNearestSymbolFrom<FuncOp>(*this, fnAttr);
637 if (!fn)
638 return emitOpError() << "'" << fnAttr.getValue()
639 << "' does not reference a valid function";
640
641 // Verify that the operand and result types match the callee.
642 auto fnType = fn.getFunctionType();
643 if (fnType.getNumInputs() != getNumOperands())
644 return emitOpError("incorrect number of operands for callee");
645
646 for (unsigned i = 0, e = fnType.getNumInputs(); i != e; ++i)
647 if (getOperand(i).getType() != fnType.getInput(i))
648 return emitOpError("operand type mismatch: expected operand type ")
649 << fnType.getInput(i) << ", but provided "
650 << getOperand(i).getType() << " for operand number " << i;
651
652 if (fnType.getNumResults() != getNumResults())
653 return emitOpError("incorrect number of results for callee");
654
655 for (unsigned i = 0, e = fnType.getNumResults(); i != e; ++i)
656 if (getResult(i).getType() != fnType.getResult(i)) {
657 auto diag = emitOpError("result type mismatch at index ") << i;
658 diag.attachNote() << " op result types: " << getResultTypes();
659 diag.attachNote() << "function result types: " << fnType.getResults();
660 return diag;
661 }
662
663 return success();
664}
665
666FunctionType CallOp::getCalleeType() {
667 return FunctionType::get(getContext(), getOperandTypes(), getResultTypes());
668}
669
670//===----------------------------------------------------------------------===//
671// DeclareFuncOp
672//===----------------------------------------------------------------------===//
673
674LogicalResult
675DeclareFuncOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
676 // Check that the sym_name attribute was specified.
677 auto fnAttr = getSymNameAttr();
678 if (!fnAttr)
679 return emitOpError("requires a 'sym_name' symbol reference attribute");
680 FuncOp fn = symbolTable.lookupNearestSymbolFrom<FuncOp>(*this, fnAttr);
681 if (!fn)
682 return emitOpError() << "'" << fnAttr.getValue()
683 << "' does not reference a valid function";
684
685 return success();
686}
687
688//===----------------------------------------------------------------------===//
689// FuncOp
690//===----------------------------------------------------------------------===//
691
692void FuncOp::build(OpBuilder &builder, OperationState &state, StringRef name,
693 FunctionType type, ArrayRef<NamedAttribute> attrs,
694 ArrayRef<DictionaryAttr> argAttrs) {
696 builder.getStringAttr(name));
697 state.addAttribute(getFunctionTypeAttrName(state.name), TypeAttr::get(type));
698 state.attributes.append(attrs.begin(), attrs.end());
699 state.addRegion();
700
701 if (argAttrs.empty())
702 return;
703 assert(type.getNumInputs() == argAttrs.size());
705 builder, state, argAttrs, /*resultAttrs=*/{},
706 getArgAttrsAttrName(state.name), getResAttrsAttrName(state.name));
707}
708
709ParseResult FuncOp::parse(OpAsmParser &parser, OperationState &result) {
710 auto buildFuncType =
711 [](Builder &builder, ArrayRef<Type> argTypes, ArrayRef<Type> results,
713 std::string &) { return builder.getFunctionType(argTypes, results); };
714
716 parser, result, /*allowVariadic=*/false,
717 getFunctionTypeAttrName(result.name), buildFuncType,
718 getArgAttrsAttrName(result.name), getResAttrsAttrName(result.name));
719}
720
721void FuncOp::print(OpAsmPrinter &p) {
723 p, *this, /*isVariadic=*/false, getFunctionTypeAttrName(),
724 getArgAttrsAttrName(), getResAttrsAttrName());
725}
726
727LogicalResult FuncOp::verify() {
728 if (llvm::any_of(getArgumentTypes(), llvm::IsaPred<LValueType>)) {
729 return emitOpError("cannot have lvalue type as argument");
730 }
731
732 if (getNumResults() > 1)
733 return emitOpError("requires zero or exactly one result, but has ")
734 << getNumResults();
735
736 if (getNumResults() == 1 && isa<ArrayType>(getResultTypes()[0]))
737 return emitOpError("cannot return array type");
738
739 return success();
740}
741
742//===----------------------------------------------------------------------===//
743// ReturnOp
744//===----------------------------------------------------------------------===//
745
746LogicalResult ReturnOp::verify() {
747 auto function = cast<FuncOp>((*this)->getParentOp());
748
749 // The operand number and types must match the function signature.
750 if (getNumOperands() != function.getNumResults())
751 return emitOpError("has ")
752 << getNumOperands() << " operands, but enclosing function (@"
753 << function.getName() << ") returns " << function.getNumResults();
754
755 if (function.getNumResults() == 1)
756 if (getOperand().getType() != function.getResultTypes()[0])
757 return emitError() << "type of the return operand ("
758 << getOperand().getType()
759 << ") doesn't match function result type ("
760 << function.getResultTypes()[0] << ")"
761 << " in function @" << function.getName();
762 return success();
763}
764
765//===----------------------------------------------------------------------===//
766// IfOp
767//===----------------------------------------------------------------------===//
768
769void IfOp::build(OpBuilder &builder, OperationState &result, Value cond,
770 bool addThenBlock, bool addElseBlock) {
771 assert((!addElseBlock || addThenBlock) &&
772 "must not create else block w/o then block");
773 result.addOperands(cond);
774
775 // Add regions and blocks.
776 OpBuilder::InsertionGuard guard(builder);
777 Region *thenRegion = result.addRegion();
778 if (addThenBlock)
779 builder.createBlock(thenRegion);
780 Region *elseRegion = result.addRegion();
781 if (addElseBlock)
782 builder.createBlock(elseRegion);
783}
784
785void IfOp::build(OpBuilder &builder, OperationState &result, Value cond,
786 bool withElseRegion) {
787 result.addOperands(cond);
788
789 // Build then region.
790 OpBuilder::InsertionGuard guard(builder);
791 Region *thenRegion = result.addRegion();
792 builder.createBlock(thenRegion);
793
794 // Build else region.
795 Region *elseRegion = result.addRegion();
796 if (withElseRegion) {
797 builder.createBlock(elseRegion);
798 }
799}
800
801void IfOp::build(OpBuilder &builder, OperationState &result, Value cond,
802 function_ref<void(OpBuilder &, Location)> thenBuilder,
803 function_ref<void(OpBuilder &, Location)> elseBuilder) {
804 assert(thenBuilder && "the builder callback for 'then' must be present");
805 result.addOperands(cond);
806
807 // Build then region.
808 OpBuilder::InsertionGuard guard(builder);
809 Region *thenRegion = result.addRegion();
810 builder.createBlock(thenRegion);
811 thenBuilder(builder, result.location);
812
813 // Build else region.
814 Region *elseRegion = result.addRegion();
815 if (elseBuilder) {
816 builder.createBlock(elseRegion);
817 elseBuilder(builder, result.location);
818 }
819}
820
821ParseResult IfOp::parse(OpAsmParser &parser, OperationState &result) {
822 // Create the regions for 'then'.
823 result.regions.reserve(2);
824 Region *thenRegion = result.addRegion();
825 Region *elseRegion = result.addRegion();
826
827 Builder &builder = parser.getBuilder();
829 Type i1Type = builder.getIntegerType(1);
830 if (parser.parseOperand(cond) ||
831 parser.resolveOperand(cond, i1Type, result.operands))
832 return failure();
833 // Parse the 'then' region.
834 if (parser.parseRegion(*thenRegion, /*arguments=*/{}, /*argTypes=*/{}))
835 return failure();
836 IfOp::ensureTerminator(*thenRegion, parser.getBuilder(), result.location);
837
838 // If we find an 'else' keyword then parse the 'else' region.
839 if (!parser.parseOptionalKeyword("else")) {
840 if (parser.parseRegion(*elseRegion, /*arguments=*/{}, /*argTypes=*/{}))
841 return failure();
842 IfOp::ensureTerminator(*elseRegion, parser.getBuilder(), result.location);
843 }
844
845 // Parse the optional attribute list.
846 if (parser.parseOptionalAttrDict(result.attributes))
847 return failure();
848 return success();
849}
850
851void IfOp::print(OpAsmPrinter &p) {
852 bool printBlockTerminators = false;
853
854 p << " " << getCondition();
855 p << ' ';
856 p.printRegion(getThenRegion(),
857 /*printEntryBlockArgs=*/false,
858 /*printBlockTerminators=*/printBlockTerminators);
859
860 // Print the 'else' regions if it exists and has a block.
861 Region &elseRegion = getElseRegion();
862 if (!elseRegion.empty()) {
863 p << " else ";
864 p.printRegion(elseRegion,
865 /*printEntryBlockArgs=*/false,
866 /*printBlockTerminators=*/printBlockTerminators);
867 }
868
869 p.printOptionalAttrDict((*this)->getAttrs());
870}
871
872/// Given the region at `index`, or the parent operation if `index` is None,
873/// return the successor regions. These are the regions that may be selected
874/// during the flow of control. `operands` is a set of optional attributes
875/// that correspond to a constant value for each operand, or null if that
876/// operand is not a constant.
877void IfOp::getSuccessorRegions(RegionBranchPoint point,
879 // The `then` and the `else` region branch back to the parent operation.
880 if (!point.isParent()) {
881 regions.push_back(RegionSuccessor::parent());
882 return;
883 }
884
885 regions.push_back(RegionSuccessor(&getThenRegion()));
886
887 // Don't consider the else region if it is empty.
888 Region *elseRegion = &this->getElseRegion();
889 if (elseRegion->empty())
890 regions.push_back(RegionSuccessor::parent());
891 else
892 regions.push_back(RegionSuccessor(elseRegion));
893}
894
895ValueRange IfOp::getSuccessorInputs(RegionSuccessor successor) {
896 return successor.isParent() ? ValueRange(getOperation()->getResults())
897 : ValueRange();
898}
899
900void IfOp::getEntrySuccessorRegions(ArrayRef<Attribute> operands,
902 FoldAdaptor adaptor(operands, *this);
903 auto boolAttr = dyn_cast_or_null<BoolAttr>(adaptor.getCondition());
904 if (!boolAttr || boolAttr.getValue())
905 regions.emplace_back(&getThenRegion());
906
907 // If the else region is empty, execution continues after the parent op.
908 if (!boolAttr || !boolAttr.getValue()) {
909 if (!getElseRegion().empty())
910 regions.emplace_back(&getElseRegion());
911 else
912 regions.emplace_back(RegionSuccessor::parent());
913 }
914}
915
916void IfOp::getRegionInvocationBounds(
917 ArrayRef<Attribute> operands,
918 SmallVectorImpl<InvocationBounds> &invocationBounds) {
919 if (auto cond = llvm::dyn_cast_or_null<BoolAttr>(operands[0])) {
920 // If the condition is known, then one region is known to be executed once
921 // and the other zero times.
922 invocationBounds.emplace_back(0, cond.getValue() ? 1 : 0);
923 invocationBounds.emplace_back(0, cond.getValue() ? 0 : 1);
924 } else {
925 // Non-constant condition. Each region may be executed 0 or 1 times.
926 invocationBounds.assign(2, {0, 1});
927 }
928}
929
930//===----------------------------------------------------------------------===//
931// IncludeOp
932//===----------------------------------------------------------------------===//
933
934void IncludeOp::print(OpAsmPrinter &p) {
935 bool standardInclude = getIsStandardInclude();
936
937 p << " ";
938 if (standardInclude)
939 p << "<";
940 p << "\"" << getInclude() << "\"";
941 if (standardInclude)
942 p << ">";
943}
944
945ParseResult IncludeOp::parse(OpAsmParser &parser, OperationState &result) {
946 bool standardInclude = !parser.parseOptionalLess();
947
948 StringAttr include;
949 OptionalParseResult includeParseResult =
950 parser.parseOptionalAttribute(include, "include", result.attributes);
951 if (!includeParseResult.has_value())
952 return parser.emitError(parser.getNameLoc()) << "expected string attribute";
953
954 if (standardInclude && parser.parseOptionalGreater())
955 return parser.emitError(parser.getNameLoc())
956 << "expected trailing '>' for standard include";
957
958 if (standardInclude)
959 result.addAttribute("is_standard_include",
960 UnitAttr::get(parser.getContext()));
961
962 return success();
963}
964
965//===----------------------------------------------------------------------===//
966// LiteralOp
967//===----------------------------------------------------------------------===//
968
969/// The literal op requires a non-empty value.
970LogicalResult emitc::LiteralOp::verify() {
971 if (getValue().empty())
972 return emitOpError() << "value must not be empty";
973 return success();
974}
975//===----------------------------------------------------------------------===//
976// SubOp
977//===----------------------------------------------------------------------===//
978
979LogicalResult SubOp::verify() {
980 Type lhsType = getLhs().getType();
981 Type rhsType = getRhs().getType();
982 Type resultType = getResult().getType();
983
984 if (isa<emitc::PointerType>(rhsType) && !isa<emitc::PointerType>(lhsType))
985 return emitOpError("rhs can only be a pointer if lhs is a pointer");
986
987 if (isa<emitc::PointerType>(lhsType) &&
988 !isa<IntegerType, emitc::OpaqueType, emitc::PointerType>(rhsType))
989 return emitOpError("requires that rhs is an integer, pointer or of opaque "
990 "type if lhs is a pointer");
991
992 if (isa<emitc::PointerType>(lhsType) && isa<emitc::PointerType>(rhsType) &&
993 !isa<IntegerType, emitc::PtrDiffTType, emitc::OpaqueType>(resultType))
994 return emitOpError("requires that the result is an integer, ptrdiff_t or "
995 "of opaque type if lhs and rhs are pointers");
996 return success();
997}
998
999//===----------------------------------------------------------------------===//
1000// VariableOp
1001//===----------------------------------------------------------------------===//
1002
1003LogicalResult emitc::VariableOp::verify() {
1004 return verifyInitializationAttribute(getOperation(), getValueAttr());
1005}
1006
1007//===----------------------------------------------------------------------===//
1008// YieldOp
1009//===----------------------------------------------------------------------===//
1010
1011LogicalResult emitc::YieldOp::verify() {
1012 Value result = getResult();
1013 Operation *containingOp = getOperation()->getParentOp();
1014
1015 if (!isa<DoOp>(containingOp) && result && containingOp->getNumResults() != 1)
1016 return emitOpError() << "yields a value not returned by parent";
1017
1018 if (!isa<DoOp>(containingOp) && !result && containingOp->getNumResults() != 0)
1019 return emitOpError() << "does not yield a value to be returned by parent";
1020
1021 return success();
1022}
1023
1024//===----------------------------------------------------------------------===//
1025// SubscriptOp
1026//===----------------------------------------------------------------------===//
1027
1028LogicalResult emitc::SubscriptOp::verify() {
1029 // Checks for array operand.
1030 if (auto arrayType = llvm::dyn_cast<emitc::ArrayType>(getValue().getType())) {
1031 // Check number of indices.
1032 if (getIndices().size() != (size_t)arrayType.getRank()) {
1033 return emitOpError() << "on array operand requires number of indices ("
1034 << getIndices().size()
1035 << ") to match the rank of the array type ("
1036 << arrayType.getRank() << ")";
1037 }
1038 // Check types of index operands.
1039 for (unsigned i = 0, e = getIndices().size(); i != e; ++i) {
1040 Type type = getIndices()[i].getType();
1041 if (!isIntegerIndexOrOpaqueType(type)) {
1042 return emitOpError() << "on array operand requires index operand " << i
1043 << " to be integer-like, but got " << type;
1044 }
1045 }
1046 // Check element type.
1047 Type elementType = arrayType.getElementType();
1048 Type resultType = getType().getValueType();
1049 if (elementType != resultType) {
1050 return emitOpError() << "on array operand requires element type ("
1051 << elementType << ") and result type (" << resultType
1052 << ") to match";
1053 }
1054 return success();
1055 }
1056
1057 // Checks for pointer operand.
1058 if (auto pointerType =
1059 llvm::dyn_cast<emitc::PointerType>(getValue().getType())) {
1060 // Check number of indices.
1061 if (getIndices().size() != 1) {
1062 return emitOpError()
1063 << "on pointer operand requires one index operand, but got "
1064 << getIndices().size();
1065 }
1066 // Check types of index operand.
1067 Type type = getIndices()[0].getType();
1068 if (!isIntegerIndexOrOpaqueType(type)) {
1069 return emitOpError() << "on pointer operand requires index operand to be "
1070 "integer-like, but got "
1071 << type;
1072 }
1073 // Check pointee type.
1074 Type pointeeType = pointerType.getPointee();
1075 Type resultType = getType().getValueType();
1076 if (pointeeType != resultType) {
1077 return emitOpError() << "on pointer operand requires pointee type ("
1078 << pointeeType << ") and result type (" << resultType
1079 << ") to match";
1080 }
1081 return success();
1082 }
1083
1084 // The operand has opaque type, so we can't assume anything about the number
1085 // or types of index operands.
1086 return success();
1087}
1088
1089//===----------------------------------------------------------------------===//
1090// VerbatimOp
1091//===----------------------------------------------------------------------===//
1092
1093LogicalResult emitc::VerbatimOp::verify() {
1094 auto errorCallback = [&]() -> InFlightDiagnostic {
1095 return this->emitOpError();
1096 };
1097 FailureOr<SmallVector<ReplacementItem>> fmt =
1098 ::parseFormatString(getValue(), getFmtArgs(), errorCallback);
1099 if (failed(fmt))
1100 return failure();
1101
1102 size_t numPlaceholders = llvm::count_if(*fmt, [](ReplacementItem &item) {
1103 return std::holds_alternative<Placeholder>(item);
1104 });
1105
1106 if (numPlaceholders != getFmtArgs().size()) {
1107 return emitOpError()
1108 << "requires operands for each placeholder in the format string";
1109 }
1110 return success();
1111}
1112
1113FailureOr<SmallVector<ReplacementItem>> emitc::VerbatimOp::parseFormatString() {
1114 // Error checking is done in verify.
1115 return ::parseFormatString(getValue(), getFmtArgs());
1116}
1117
1118//===----------------------------------------------------------------------===//
1119// EmitC Enums
1120//===----------------------------------------------------------------------===//
1121
1122#include "mlir/Dialect/EmitC/IR/EmitCEnums.cpp.inc"
1123
1124//===----------------------------------------------------------------------===//
1125// EmitC Attributes
1126//===----------------------------------------------------------------------===//
1127
1128#define GET_ATTRDEF_CLASSES
1129#include "mlir/Dialect/EmitC/IR/EmitCAttributes.cpp.inc"
1130
1131//===----------------------------------------------------------------------===//
1132// EmitC Types
1133//===----------------------------------------------------------------------===//
1134
1135#define GET_TYPEDEF_CLASSES
1136#include "mlir/Dialect/EmitC/IR/EmitCTypes.cpp.inc"
1137
1138//===----------------------------------------------------------------------===//
1139// ArrayType
1140//===----------------------------------------------------------------------===//
1141
1142Type emitc::ArrayType::parse(AsmParser &parser) {
1143 if (parser.parseLess())
1144 return Type();
1145
1146 SmallVector<int64_t, 4> dimensions;
1147 if (parser.parseDimensionList(dimensions, /*allowDynamic=*/false,
1148 /*withTrailingX=*/true))
1149 return Type();
1150 // Parse the element type.
1151 auto typeLoc = parser.getCurrentLocation();
1152 Type elementType;
1153 if (parser.parseType(elementType))
1154 return Type();
1155
1156 // Check that array is formed from allowed types.
1157 if (!isValidElementType(elementType))
1158 return parser.emitError(typeLoc, "invalid array element type '")
1159 << elementType << "'",
1160 Type();
1161 if (parser.parseGreater())
1162 return Type();
1163 return parser.getChecked<ArrayType>(dimensions, elementType);
1164}
1165
1166void emitc::ArrayType::print(AsmPrinter &printer) const {
1167 printer << "<";
1168 for (int64_t dim : getShape()) {
1169 printer << dim << 'x';
1170 }
1171 printer.printType(getElementType());
1172 printer << ">";
1173}
1174
1175LogicalResult emitc::ArrayType::verify(
1177 ::llvm::ArrayRef<int64_t> shape, Type elementType) {
1178 if (shape.empty())
1179 return emitError() << "shape must not be empty";
1180
1181 for (int64_t dim : shape) {
1182 if (dim < 0)
1183 return emitError() << "dimensions must have non-negative size";
1184 }
1185
1186 if (!elementType)
1187 return emitError() << "element type must not be none";
1188
1189 if (!isValidElementType(elementType))
1190 return emitError() << "invalid array element type";
1191
1192 return success();
1193}
1194
1195emitc::ArrayType
1196emitc::ArrayType::cloneWith(std::optional<ArrayRef<int64_t>> shape,
1197 Type elementType) const {
1198 if (!shape)
1199 return emitc::ArrayType::get(getShape(), elementType);
1200 return emitc::ArrayType::get(*shape, elementType);
1201}
1202
1203//===----------------------------------------------------------------------===//
1204// LValueType
1205//===----------------------------------------------------------------------===//
1206
1207LogicalResult mlir::emitc::LValueType::verify(
1209 mlir::Type value) {
1210 // Check that the wrapped type is valid. This especially forbids nested
1211 // lvalue types.
1212 if (!isSupportedEmitCType(value))
1213 return emitError()
1214 << "!emitc.lvalue must wrap supported emitc type, but got " << value;
1215
1216 if (llvm::isa<emitc::ArrayType>(value))
1217 return emitError() << "!emitc.lvalue cannot wrap !emitc.array type";
1218
1219 return success();
1220}
1221
1222//===----------------------------------------------------------------------===//
1223// OpaqueType
1224//===----------------------------------------------------------------------===//
1225
1226LogicalResult mlir::emitc::OpaqueType::verify(
1228 llvm::StringRef value) {
1229 if (value.empty()) {
1230 return emitError() << "expected non empty string in !emitc.opaque type";
1231 }
1232 if (value.back() == '*') {
1233 return emitError() << "pointer not allowed as outer type with "
1234 "!emitc.opaque, use !emitc.ptr instead";
1235 }
1236 return success();
1237}
1238
1239//===----------------------------------------------------------------------===//
1240// PointerType
1241//===----------------------------------------------------------------------===//
1242
1243LogicalResult mlir::emitc::PointerType::verify(
1245 if (llvm::isa<emitc::LValueType>(value))
1246 return emitError() << "pointers to lvalues are not allowed";
1247
1248 return success();
1249}
1250
1251//===----------------------------------------------------------------------===//
1252// GlobalOp
1253//===----------------------------------------------------------------------===//
1255 TypeAttr type,
1256 Attribute initialValue) {
1257 p << type;
1258 if (initialValue) {
1259 p << " = ";
1260 p.printAttributeWithoutType(initialValue);
1261 }
1262}
1263
1265 if (auto array = llvm::dyn_cast<ArrayType>(type))
1266 return RankedTensorType::get(array.getShape(), array.getElementType());
1267 return type;
1268}
1269
1270static ParseResult
1272 Attribute &initialValue) {
1273 Type type;
1274 if (parser.parseType(type))
1275 return failure();
1276
1277 typeAttr = TypeAttr::get(type);
1278
1279 if (parser.parseOptionalEqual())
1280 return success();
1281
1282 if (parser.parseAttribute(initialValue, getInitializerTypeForGlobal(type)))
1283 return failure();
1284
1285 if (!llvm::isa<ElementsAttr, IntegerAttr, FloatAttr, emitc::OpaqueAttr>(
1286 initialValue))
1287 return parser.emitError(parser.getNameLoc())
1288 << "initial value should be a integer, float, elements or opaque "
1289 "attribute";
1290 return success();
1291}
1292
1293LogicalResult GlobalOp::verify() {
1294 if (!isSupportedEmitCType(getType())) {
1295 return emitOpError("expected valid emitc type");
1296 }
1297 if (getInitialValue().has_value()) {
1298 Attribute initValue = getInitialValue().value();
1299 // Check that the type of the initial value is compatible with the type of
1300 // the global variable.
1301 if (auto elementsAttr = llvm::dyn_cast<ElementsAttr>(initValue)) {
1302 auto arrayType = llvm::dyn_cast<ArrayType>(getType());
1303 if (!arrayType)
1304 return emitOpError("expected array type, but got ") << getType();
1305
1306 Type initType = elementsAttr.getType();
1308 if (initType != tensorType) {
1309 return emitOpError("initial value expected to be of type ")
1310 << getType() << ", but was of type " << initType;
1311 }
1312 } else if (auto intAttr = dyn_cast<IntegerAttr>(initValue)) {
1313 if (intAttr.getType() != getType()) {
1314 return emitOpError("initial value expected to be of type ")
1315 << getType() << ", but was of type " << intAttr.getType();
1316 }
1317 } else if (auto floatAttr = dyn_cast<FloatAttr>(initValue)) {
1318 if (floatAttr.getType() != getType()) {
1319 return emitOpError("initial value expected to be of type ")
1320 << getType() << ", but was of type " << floatAttr.getType();
1321 }
1322 } else if (!isa<emitc::OpaqueAttr>(initValue)) {
1323 return emitOpError("initial value should be a integer, float, elements "
1324 "or opaque attribute, but got ")
1325 << initValue;
1326 }
1327 }
1328 if (getStaticSpecifier() && getExternSpecifier()) {
1329 return emitOpError("cannot have both static and extern specifiers");
1330 }
1331 return success();
1332}
1333
1334//===----------------------------------------------------------------------===//
1335// GetGlobalOp
1336//===----------------------------------------------------------------------===//
1337
1338LogicalResult
1339GetGlobalOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1340 // Verify that the type matches the type of the global variable.
1341 auto global =
1342 symbolTable.lookupNearestSymbolFrom<GlobalOp>(*this, getNameAttr());
1343 if (!global)
1344 return emitOpError("'")
1345 << getName() << "' does not reference a valid emitc.global";
1346
1347 Type resultType = getResult().getType();
1348 Type globalType = global.getType();
1349
1350 // global has array type
1351 if (llvm::isa<ArrayType>(globalType)) {
1352 if (globalType != resultType)
1353 return emitOpError("on array type expects result type ")
1354 << resultType << " to match type " << globalType
1355 << " of the global @" << getName();
1356 return success();
1357 }
1358
1359 // global has non-array type
1360 auto lvalueType = dyn_cast<LValueType>(resultType);
1361 if (!lvalueType)
1362 return emitOpError("on non-array type expects result type to be an "
1363 "lvalue type for the global @")
1364 << getName();
1365 if (lvalueType.getValueType() != globalType)
1366 return emitOpError("on non-array type expects result inner type ")
1367 << lvalueType.getValueType() << " to match type " << globalType
1368 << " of the global @" << getName();
1369 return success();
1370}
1371
1372//===----------------------------------------------------------------------===//
1373// SwitchOp
1374//===----------------------------------------------------------------------===//
1375
1376/// Parse the case regions and values.
1377static ParseResult
1379 SmallVectorImpl<std::unique_ptr<Region>> &caseRegions) {
1380 SmallVector<int64_t> caseValues;
1381 while (succeeded(parser.parseOptionalKeyword("case"))) {
1382 int64_t value;
1383 Region &region = *caseRegions.emplace_back(std::make_unique<Region>());
1384 if (parser.parseInteger(value) ||
1385 parser.parseRegion(region, /*arguments=*/{}))
1386 return failure();
1387 caseValues.push_back(value);
1388 }
1389 cases = parser.getBuilder().getDenseI64ArrayAttr(caseValues);
1390 return success();
1391}
1392
1393/// Print the case regions and values.
1395 DenseI64ArrayAttr cases, RegionRange caseRegions) {
1396 for (auto [value, region] : llvm::zip(cases.asArrayRef(), caseRegions)) {
1397 p.printNewline();
1398 p << "case " << value << ' ';
1399 p.printRegion(*region, /*printEntryBlockArgs=*/false);
1400 }
1401}
1402
1403static LogicalResult verifyRegion(emitc::SwitchOp op, Region &region,
1404 const Twine &name) {
1405 auto yield = dyn_cast<emitc::YieldOp>(region.front().back());
1406 if (!yield)
1407 return op.emitOpError("expected region to end with emitc.yield, but got ")
1408 << region.front().back().getName();
1409
1410 if (yield.getNumOperands() != 0) {
1411 return (op.emitOpError("expected each region to return ")
1412 << "0 values, but " << name << " returns "
1413 << yield.getNumOperands())
1414 .attachNote(yield.getLoc())
1415 << "see yield operation here";
1416 }
1417
1418 return success();
1419}
1420
1421LogicalResult emitc::SwitchOp::verify() {
1422 if (!isIntegerIndexOrOpaqueType(getArg().getType()))
1423 return emitOpError("unsupported type ") << getArg().getType();
1424
1425 if (getCases().size() != getCaseRegions().size()) {
1426 return emitOpError("has ")
1427 << getCaseRegions().size() << " case regions but "
1428 << getCases().size() << " case values";
1429 }
1430
1431 DenseSet<int64_t> valueSet;
1432 for (int64_t value : getCases())
1433 if (!valueSet.insert(value).second)
1434 return emitOpError("has duplicate case value: ") << value;
1435
1436 if (failed(verifyRegion(*this, getDefaultRegion(), "default region")))
1437 return failure();
1438
1439 for (auto [idx, caseRegion] : llvm::enumerate(getCaseRegions()))
1440 if (failed(verifyRegion(*this, caseRegion, "case region #" + Twine(idx))))
1441 return failure();
1442
1443 return success();
1444}
1445
1446unsigned emitc::SwitchOp::getNumCases() { return getCases().size(); }
1447
1448Block &emitc::SwitchOp::getDefaultBlock() { return getDefaultRegion().front(); }
1449
1450Block &emitc::SwitchOp::getCaseBlock(unsigned idx) {
1451 assert(idx < getNumCases() && "case index out-of-bounds");
1452 return getCaseRegions()[idx].front();
1453}
1454
1455void SwitchOp::getSuccessorRegions(
1457 llvm::append_range(successors, getRegions());
1458}
1459
1460void SwitchOp::getEntrySuccessorRegions(
1461 ArrayRef<Attribute> operands,
1463 FoldAdaptor adaptor(operands, *this);
1464
1465 // If a constant was not provided, all regions are possible successors.
1466 auto arg = dyn_cast_or_null<IntegerAttr>(adaptor.getArg());
1467 if (!arg) {
1468 llvm::append_range(successors, getRegions());
1469 return;
1470 }
1471
1472 // Otherwise, try to find a case with a matching value. If not, the
1473 // default region is the only successor.
1474 for (auto [caseValue, caseRegion] : llvm::zip(getCases(), getCaseRegions())) {
1475 if (caseValue == arg.getInt()) {
1476 successors.emplace_back(&caseRegion);
1477 return;
1478 }
1479 }
1480 successors.emplace_back(&getDefaultRegion());
1481}
1482
1483void SwitchOp::getRegionInvocationBounds(
1485 auto operandValue = llvm::dyn_cast_or_null<IntegerAttr>(operands.front());
1486 if (!operandValue) {
1487 // All regions are invoked at most once.
1488 bounds.append(getNumRegions(), InvocationBounds(/*lb=*/0, /*ub=*/1));
1489 return;
1490 }
1491
1492 unsigned liveIndex = getNumRegions() - 1;
1493 const auto *iteratorToInt = llvm::find(getCases(), operandValue.getInt());
1494
1495 liveIndex = iteratorToInt != getCases().end()
1496 ? std::distance(getCases().begin(), iteratorToInt)
1497 : liveIndex;
1498
1499 for (unsigned regIndex = 0, regNum = getNumRegions(); regIndex < regNum;
1500 ++regIndex)
1501 bounds.emplace_back(/*lb=*/0, /*ub=*/regIndex == liveIndex);
1502}
1503
1504//===----------------------------------------------------------------------===//
1505// FileOp
1506//===----------------------------------------------------------------------===//
1507void FileOp::build(OpBuilder &builder, OperationState &state, StringRef id) {
1508 state.addRegion()->emplaceBlock();
1509 state.attributes.push_back(
1510 builder.getNamedAttr("id", builder.getStringAttr(id)));
1511}
1512
1513//===----------------------------------------------------------------------===//
1514// FieldOp
1515//===----------------------------------------------------------------------===//
1516
1518 TypeAttr type,
1519 Attribute initialValue) {
1520 p << type;
1521 if (initialValue) {
1522 p << " = ";
1523 p.printAttributeWithoutType(initialValue);
1524 }
1525}
1526
1528 if (auto array = llvm::dyn_cast<ArrayType>(type))
1529 return RankedTensorType::get(array.getShape(), array.getElementType());
1530 return type;
1531}
1532
1533static ParseResult
1535 Attribute &initialValue) {
1536 Type type;
1537 if (parser.parseType(type))
1538 return failure();
1539
1540 typeAttr = TypeAttr::get(type);
1541
1542 if (parser.parseOptionalEqual())
1543 return success();
1544
1545 if (parser.parseAttribute(initialValue, getInitializerTypeForField(type)))
1546 return failure();
1547
1548 if (!llvm::isa<ElementsAttr, IntegerAttr, FloatAttr, emitc::OpaqueAttr>(
1549 initialValue))
1550 return parser.emitError(parser.getNameLoc())
1551 << "initial value should be a integer, float, elements or opaque "
1552 "attribute";
1553 return success();
1554}
1555
1556LogicalResult FieldOp::verify() {
1558 return emitOpError("expected valid emitc type");
1559
1560 Operation *parentOp = getOperation()->getParentOp();
1561 if (!parentOp || !isa<emitc::ClassOp>(parentOp))
1562 return emitOpError("field must be nested within an emitc.class operation");
1563
1564 StringAttr symName = getSymNameAttr();
1565 if (!symName || symName.getValue().empty())
1566 return emitOpError("field must have a non-empty symbol name");
1567
1568 return success();
1569}
1570
1571//===----------------------------------------------------------------------===//
1572// GetFieldOp
1573//===----------------------------------------------------------------------===//
1574
1575LogicalResult GetFieldOp::verify() {
1576 auto parentClassOp = getOperation()->getParentOfType<emitc::ClassOp>();
1577 if (!parentClassOp.getOperation())
1578 return emitOpError(" must be nested within an emitc.class operation");
1579
1580 return success();
1581}
1582
1583LogicalResult GetFieldOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1584 mlir::FlatSymbolRefAttr fieldNameAttr = getFieldNameAttr();
1585 FieldOp fieldOp =
1586 symbolTable.lookupNearestSymbolFrom<FieldOp>(*this, fieldNameAttr);
1587 if (!fieldOp)
1588 return emitOpError("field '")
1589 << fieldNameAttr << "' not found in the class";
1590
1591 Type getFieldResultType = getResult().getType();
1592 Type fieldType = fieldOp.getType();
1593
1594 if (fieldType != getFieldResultType)
1595 return emitOpError("result type ")
1596 << getFieldResultType << " does not match field '" << fieldNameAttr
1597 << "' type " << fieldType;
1598
1599 return success();
1600}
1601
1602//===----------------------------------------------------------------------===//
1603// DoOp
1604//===----------------------------------------------------------------------===//
1605
1606void DoOp::print(OpAsmPrinter &p) {
1607 p << ' ';
1608 p.printRegion(getBodyRegion(), /*printEntryBlockArgs=*/false);
1609 p << " while ";
1610 p.printRegion(getConditionRegion());
1611 p.printOptionalAttrDictWithKeyword(getOperation()->getAttrs());
1612}
1613
1614LogicalResult emitc::DoOp::verify() {
1615 Block &condBlock = getConditionRegion().front();
1616
1617 if (condBlock.getOperations().size() != 2)
1618 return emitOpError(
1619 "condition region must contain exactly two operations: "
1620 "'emitc.expression' followed by 'emitc.yield', but found ")
1621 << condBlock.getOperations().size() << " operations";
1622
1623 Operation &first = condBlock.front();
1624 auto exprOp = dyn_cast<emitc::ExpressionOp>(first);
1625 if (!exprOp)
1626 return emitOpError("expected first op in condition region to be "
1627 "'emitc.expression', but got ")
1628 << first.getName();
1629
1630 if (!exprOp.getResult().getType().isInteger(1))
1631 return emitOpError("emitc.expression in condition region must return "
1632 "'i1', but returns ")
1633 << exprOp.getResult().getType();
1634
1635 Operation &last = condBlock.back();
1636 auto condYield = dyn_cast<emitc::YieldOp>(last);
1637 if (!condYield)
1638 return emitOpError("expected last op in condition region to be "
1639 "'emitc.yield', but got ")
1640 << last.getName();
1641
1642 if (condYield.getNumOperands() != 1)
1643 return emitOpError("expected condition region to return 1 value, but "
1644 "it returns ")
1645 << condYield.getNumOperands() << " values";
1646
1647 if (condYield.getOperand(0) != exprOp.getResult())
1648 return emitError("'emitc.yield' must return result of "
1649 "'emitc.expression' from this condition region");
1650
1651 Block &bodyBlock = getBodyRegion().front();
1652 if (bodyBlock.mightHaveTerminator())
1653 return emitOpError("body region must not contain terminator");
1654
1655 return success();
1656}
1657
1658ParseResult DoOp::parse(OpAsmParser &parser, OperationState &result) {
1659 Region *bodyRegion = result.addRegion();
1660 Region *condRegion = result.addRegion();
1661
1662 if (parser.parseRegion(*bodyRegion) || parser.parseKeyword("while") ||
1663 parser.parseRegion(*condRegion))
1664 return failure();
1665
1666 if (bodyRegion->empty())
1667 bodyRegion->emplaceBlock();
1668
1669 return parser.parseOptionalAttrDictWithKeyword(result.attributes);
1670}
1671
1672//===----------------------------------------------------------------------===//
1673// TableGen'd op method definitions
1674//===----------------------------------------------------------------------===//
1675
1676#include "mlir/Dialect/EmitC/IR/EmitCInterfaces.cpp.inc"
1677
1678#define GET_OP_CLASSES
1679#include "mlir/Dialect/EmitC/IR/EmitC.cpp.inc"
return success()
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 bool hasSideEffects(Operation *op)
static LogicalResult verifyInitializationAttribute(Operation *op, Attribute value)
Check that the type of the initial value is compatible with the operations result type.
Definition EmitC.cpp:144
static LogicalResult verifyRegion(emitc::SwitchOp op, Region &region, const Twine &name)
Definition EmitC.cpp:1403
static ParseResult parseEmitCGlobalOpTypeAndInitialValue(OpAsmParser &parser, TypeAttr &typeAttr, Attribute &initialValue)
Definition EmitC.cpp:1271
static Type getInitializerTypeForField(Type type)
Definition EmitC.cpp:1527
static ParseResult parseEmitCFieldOpTypeAndInitialValue(OpAsmParser &parser, TypeAttr &typeAttr, Attribute &initialValue)
Definition EmitC.cpp:1534
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.
Definition EmitC.cpp:179
static void printEmitCGlobalOpTypeAndInitialValue(OpAsmPrinter &p, GlobalOp op, TypeAttr type, Attribute initialValue)
Definition EmitC.cpp:1254
static ParseResult parseSwitchCases(OpAsmParser &parser, DenseI64ArrayAttr &cases, SmallVectorImpl< std::unique_ptr< Region > > &caseRegions)
Parse the case regions and values.
Definition EmitC.cpp:1378
static void printEmitCFieldOpTypeAndInitialValue(OpAsmPrinter &p, FieldOp op, TypeAttr type, Attribute initialValue)
Definition EmitC.cpp:1517
static void printSwitchCases(OpAsmPrinter &p, Operation *op, DenseI64ArrayAttr cases, RegionRange caseRegions)
Print the case regions and values.
Definition EmitC.cpp:1394
static Type getInitializerTypeForGlobal(Type type)
Definition EmitC.cpp:1264
static Type getElementType(Type type)
Determine the element type of type.
b getContext())
static std::string diag(const llvm::Value &value)
static Type getValueType(Attribute attr)
Definition SPIRVOps.cpp:773
static ArrayRef< int64_t > getShape(Type type)
Returns the shape of the given type.
Definition Traits.cpp:117
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.
Definition Attributes.h:25
Block represents an ordered list of Operations.
Definition Block.h:33
BlockArgument getArgument(unsigned i)
Definition Block.h:139
OpListType & getOperations()
Definition Block.h:147
Operation & front()
Definition Block.h:163
Operation & back()
Definition Block.h:162
Operation * getTerminator()
Get the terminator operation of this block.
Definition Block.cpp:249
BlockArgument addArgument(Type type, Location loc)
Add one value to the argument list.
Definition Block.cpp:158
bool mightHaveTerminator()
Return "true" if this block might have a terminator.
Definition Block.cpp:255
iterator_range< iterator > without_terminator()
Return an iterator range over the operation within this block excluding the terminator operation at t...
Definition Block.h:222
This class is a general helper class for creating context-global objects like types,...
Definition Builders.h:51
UnitAttr getUnitAttr()
Definition Builders.cpp:98
DenseI64ArrayAttr getDenseI64ArrayAttr(ArrayRef< int64_t > values)
Definition Builders.cpp:167
FunctionType getFunctionType(TypeRange inputs, TypeRange results)
Definition Builders.cpp:76
IntegerType getIntegerType(unsigned width)
Definition Builders.cpp:67
StringAttr getStringAttr(const Twine &bytes)
Definition Builders.cpp:262
IndexType getIndexType()
Definition Builders.cpp:51
NamedAttribute getNamedAttr(StringRef name, Attribute val)
Definition Builders.cpp:94
A symbol reference with a reference path containing a single element.
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...
Definition Location.h:76
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
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 &region, 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 &region, 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.
Definition Builders.h:348
This class helps build Operations.
Definition Builders.h:207
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.
Definition Builders.cpp:430
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
Definition Builders.h:431
This class represents a single result from folding an operation.
type_range getType() const
Operation is the basic unit of execution within MLIR.
Definition Operation.h:88
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition Operation.h:407
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition Operation.h:234
OperationName getName()
The name of an operation is the key identifier for it.
Definition Operation.h:119
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition Operation.h:378
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.
Definition Operation.h:404
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.
Definition Region.h:346
This class represents a successor of a region.
static RegionSuccessor parent()
Initialize a successor that branches after/out of the parent operation.
bool isParent() const
Return true if the successor is the parent operation.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
Block & front()
Definition Region.h:65
Block & emplaceBlock()
Definition Region.h:46
bool empty()
Definition Region.h:60
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.
Definition SymbolTable.h:76
This class provides an abstraction over the various different ranges of value types.
Definition TypeRange.h:37
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
bool isIndex() const
Definition Types.cpp:54
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:387
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Type getType() const
Return the type of this value.
Definition Value.h:105
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
A named class for passing around the variadic flag.
mlir::Value getVar(mlir::Operation *accDataClauseOp)
Used to obtain the var from a data clause operation.
Definition OpenACC.cpp:4892
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.
Definition EmitC.cpp:57
std::variant< StringRef, Placeholder > ReplacementItem
Definition EmitC.h:54
bool isFundamentalType(mlir::Type type)
Determines whether type is a valid fundamental C++ type in EmitC.
Definition EmitC.cpp:136
bool isSupportedFloatType(mlir::Type type)
Determines whether type is a valid floating-point type in EmitC.
Definition EmitC.cpp:116
bool isSupportedEmitCType(mlir::Type type)
Determines whether type is valid in EmitC.
Definition EmitC.cpp:61
bool isPointerWideType(mlir::Type type)
Determines whether type is a emitc.size_t/ssize_t type.
Definition EmitC.cpp:131
bool isIntegerIndexOrOpaqueType(Type type)
Determines whether type is integer like, i.e.
Definition EmitC.cpp:111
bool isSupportedIntegerType(mlir::Type type)
Determines whether type is a valid integer type in EmitC.
Definition EmitC.cpp:95
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.
Definition Utils.cpp:18
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:573
Include the generated interface declarations.
detail::DenseArrayAttrImpl< int64_t > DenseI64ArrayAttr
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition Utils.cpp:304
llvm::DenseSet< ValueT, ValueInfoT > DenseSet
Definition LLVM.h:128
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
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.
Definition Value.h:497
llvm::function_ref< Fn > function_ref
Definition LLVM.h:152
This is the representation of an operand reference.
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.