MLIR 22.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(
882 RegionSuccessor(getOperation(), getOperation()->getResults()));
883 return;
884 }
885
886 regions.push_back(RegionSuccessor(&getThenRegion()));
887
888 // Don't consider the else region if it is empty.
889 Region *elseRegion = &this->getElseRegion();
890 if (elseRegion->empty())
891 regions.push_back(
892 RegionSuccessor(getOperation(), getOperation()->getResults()));
893 else
894 regions.push_back(RegionSuccessor(elseRegion));
895}
896
897void IfOp::getEntrySuccessorRegions(ArrayRef<Attribute> operands,
899 FoldAdaptor adaptor(operands, *this);
900 auto boolAttr = dyn_cast_or_null<BoolAttr>(adaptor.getCondition());
901 if (!boolAttr || boolAttr.getValue())
902 regions.emplace_back(&getThenRegion());
903
904 // If the else region is empty, execution continues after the parent op.
905 if (!boolAttr || !boolAttr.getValue()) {
906 if (!getElseRegion().empty())
907 regions.emplace_back(&getElseRegion());
908 else
909 regions.emplace_back(getOperation(), getOperation()->getResults());
910 }
911}
912
913void IfOp::getRegionInvocationBounds(
914 ArrayRef<Attribute> operands,
915 SmallVectorImpl<InvocationBounds> &invocationBounds) {
916 if (auto cond = llvm::dyn_cast_or_null<BoolAttr>(operands[0])) {
917 // If the condition is known, then one region is known to be executed once
918 // and the other zero times.
919 invocationBounds.emplace_back(0, cond.getValue() ? 1 : 0);
920 invocationBounds.emplace_back(0, cond.getValue() ? 0 : 1);
921 } else {
922 // Non-constant condition. Each region may be executed 0 or 1 times.
923 invocationBounds.assign(2, {0, 1});
924 }
925}
926
927//===----------------------------------------------------------------------===//
928// IncludeOp
929//===----------------------------------------------------------------------===//
930
931void IncludeOp::print(OpAsmPrinter &p) {
932 bool standardInclude = getIsStandardInclude();
933
934 p << " ";
935 if (standardInclude)
936 p << "<";
937 p << "\"" << getInclude() << "\"";
938 if (standardInclude)
939 p << ">";
940}
941
942ParseResult IncludeOp::parse(OpAsmParser &parser, OperationState &result) {
943 bool standardInclude = !parser.parseOptionalLess();
944
945 StringAttr include;
946 OptionalParseResult includeParseResult =
947 parser.parseOptionalAttribute(include, "include", result.attributes);
948 if (!includeParseResult.has_value())
949 return parser.emitError(parser.getNameLoc()) << "expected string attribute";
950
951 if (standardInclude && parser.parseOptionalGreater())
952 return parser.emitError(parser.getNameLoc())
953 << "expected trailing '>' for standard include";
954
955 if (standardInclude)
956 result.addAttribute("is_standard_include",
957 UnitAttr::get(parser.getContext()));
958
959 return success();
960}
961
962//===----------------------------------------------------------------------===//
963// LiteralOp
964//===----------------------------------------------------------------------===//
965
966/// The literal op requires a non-empty value.
967LogicalResult emitc::LiteralOp::verify() {
968 if (getValue().empty())
969 return emitOpError() << "value must not be empty";
970 return success();
971}
972//===----------------------------------------------------------------------===//
973// SubOp
974//===----------------------------------------------------------------------===//
975
976LogicalResult SubOp::verify() {
977 Type lhsType = getLhs().getType();
978 Type rhsType = getRhs().getType();
979 Type resultType = getResult().getType();
980
981 if (isa<emitc::PointerType>(rhsType) && !isa<emitc::PointerType>(lhsType))
982 return emitOpError("rhs can only be a pointer if lhs is a pointer");
983
984 if (isa<emitc::PointerType>(lhsType) &&
985 !isa<IntegerType, emitc::OpaqueType, emitc::PointerType>(rhsType))
986 return emitOpError("requires that rhs is an integer, pointer or of opaque "
987 "type if lhs is a pointer");
988
989 if (isa<emitc::PointerType>(lhsType) && isa<emitc::PointerType>(rhsType) &&
990 !isa<IntegerType, emitc::PtrDiffTType, emitc::OpaqueType>(resultType))
991 return emitOpError("requires that the result is an integer, ptrdiff_t or "
992 "of opaque type if lhs and rhs are pointers");
993 return success();
994}
995
996//===----------------------------------------------------------------------===//
997// VariableOp
998//===----------------------------------------------------------------------===//
999
1000LogicalResult emitc::VariableOp::verify() {
1001 return verifyInitializationAttribute(getOperation(), getValueAttr());
1002}
1003
1004//===----------------------------------------------------------------------===//
1005// YieldOp
1006//===----------------------------------------------------------------------===//
1007
1008LogicalResult emitc::YieldOp::verify() {
1009 Value result = getResult();
1010 Operation *containingOp = getOperation()->getParentOp();
1011
1012 if (!isa<DoOp>(containingOp) && result && containingOp->getNumResults() != 1)
1013 return emitOpError() << "yields a value not returned by parent";
1014
1015 if (!isa<DoOp>(containingOp) && !result && containingOp->getNumResults() != 0)
1016 return emitOpError() << "does not yield a value to be returned by parent";
1017
1018 return success();
1019}
1020
1021//===----------------------------------------------------------------------===//
1022// SubscriptOp
1023//===----------------------------------------------------------------------===//
1024
1025LogicalResult emitc::SubscriptOp::verify() {
1026 // Checks for array operand.
1027 if (auto arrayType = llvm::dyn_cast<emitc::ArrayType>(getValue().getType())) {
1028 // Check number of indices.
1029 if (getIndices().size() != (size_t)arrayType.getRank()) {
1030 return emitOpError() << "on array operand requires number of indices ("
1031 << getIndices().size()
1032 << ") to match the rank of the array type ("
1033 << arrayType.getRank() << ")";
1034 }
1035 // Check types of index operands.
1036 for (unsigned i = 0, e = getIndices().size(); i != e; ++i) {
1037 Type type = getIndices()[i].getType();
1038 if (!isIntegerIndexOrOpaqueType(type)) {
1039 return emitOpError() << "on array operand requires index operand " << i
1040 << " to be integer-like, but got " << type;
1041 }
1042 }
1043 // Check element type.
1044 Type elementType = arrayType.getElementType();
1045 Type resultType = getType().getValueType();
1046 if (elementType != resultType) {
1047 return emitOpError() << "on array operand requires element type ("
1048 << elementType << ") and result type (" << resultType
1049 << ") to match";
1050 }
1051 return success();
1052 }
1053
1054 // Checks for pointer operand.
1055 if (auto pointerType =
1056 llvm::dyn_cast<emitc::PointerType>(getValue().getType())) {
1057 // Check number of indices.
1058 if (getIndices().size() != 1) {
1059 return emitOpError()
1060 << "on pointer operand requires one index operand, but got "
1061 << getIndices().size();
1062 }
1063 // Check types of index operand.
1064 Type type = getIndices()[0].getType();
1065 if (!isIntegerIndexOrOpaqueType(type)) {
1066 return emitOpError() << "on pointer operand requires index operand to be "
1067 "integer-like, but got "
1068 << type;
1069 }
1070 // Check pointee type.
1071 Type pointeeType = pointerType.getPointee();
1072 Type resultType = getType().getValueType();
1073 if (pointeeType != resultType) {
1074 return emitOpError() << "on pointer operand requires pointee type ("
1075 << pointeeType << ") and result type (" << resultType
1076 << ") to match";
1077 }
1078 return success();
1079 }
1080
1081 // The operand has opaque type, so we can't assume anything about the number
1082 // or types of index operands.
1083 return success();
1084}
1085
1086//===----------------------------------------------------------------------===//
1087// VerbatimOp
1088//===----------------------------------------------------------------------===//
1089
1090LogicalResult emitc::VerbatimOp::verify() {
1091 auto errorCallback = [&]() -> InFlightDiagnostic {
1092 return this->emitOpError();
1093 };
1094 FailureOr<SmallVector<ReplacementItem>> fmt =
1095 ::parseFormatString(getValue(), getFmtArgs(), errorCallback);
1096 if (failed(fmt))
1097 return failure();
1098
1099 size_t numPlaceholders = llvm::count_if(*fmt, [](ReplacementItem &item) {
1100 return std::holds_alternative<Placeholder>(item);
1101 });
1102
1103 if (numPlaceholders != getFmtArgs().size()) {
1104 return emitOpError()
1105 << "requires operands for each placeholder in the format string";
1106 }
1107 return success();
1108}
1109
1110FailureOr<SmallVector<ReplacementItem>> emitc::VerbatimOp::parseFormatString() {
1111 // Error checking is done in verify.
1112 return ::parseFormatString(getValue(), getFmtArgs());
1113}
1114
1115//===----------------------------------------------------------------------===//
1116// EmitC Enums
1117//===----------------------------------------------------------------------===//
1118
1119#include "mlir/Dialect/EmitC/IR/EmitCEnums.cpp.inc"
1120
1121//===----------------------------------------------------------------------===//
1122// EmitC Attributes
1123//===----------------------------------------------------------------------===//
1124
1125#define GET_ATTRDEF_CLASSES
1126#include "mlir/Dialect/EmitC/IR/EmitCAttributes.cpp.inc"
1127
1128//===----------------------------------------------------------------------===//
1129// EmitC Types
1130//===----------------------------------------------------------------------===//
1131
1132#define GET_TYPEDEF_CLASSES
1133#include "mlir/Dialect/EmitC/IR/EmitCTypes.cpp.inc"
1134
1135//===----------------------------------------------------------------------===//
1136// ArrayType
1137//===----------------------------------------------------------------------===//
1138
1139Type emitc::ArrayType::parse(AsmParser &parser) {
1140 if (parser.parseLess())
1141 return Type();
1142
1143 SmallVector<int64_t, 4> dimensions;
1144 if (parser.parseDimensionList(dimensions, /*allowDynamic=*/false,
1145 /*withTrailingX=*/true))
1146 return Type();
1147 // Parse the element type.
1148 auto typeLoc = parser.getCurrentLocation();
1149 Type elementType;
1150 if (parser.parseType(elementType))
1151 return Type();
1152
1153 // Check that array is formed from allowed types.
1154 if (!isValidElementType(elementType))
1155 return parser.emitError(typeLoc, "invalid array element type '")
1156 << elementType << "'",
1157 Type();
1158 if (parser.parseGreater())
1159 return Type();
1160 return parser.getChecked<ArrayType>(dimensions, elementType);
1161}
1162
1163void emitc::ArrayType::print(AsmPrinter &printer) const {
1164 printer << "<";
1165 for (int64_t dim : getShape()) {
1166 printer << dim << 'x';
1167 }
1168 printer.printType(getElementType());
1169 printer << ">";
1170}
1171
1172LogicalResult emitc::ArrayType::verify(
1174 ::llvm::ArrayRef<int64_t> shape, Type elementType) {
1175 if (shape.empty())
1176 return emitError() << "shape must not be empty";
1177
1178 for (int64_t dim : shape) {
1179 if (dim < 0)
1180 return emitError() << "dimensions must have non-negative size";
1181 }
1182
1183 if (!elementType)
1184 return emitError() << "element type must not be none";
1185
1186 if (!isValidElementType(elementType))
1187 return emitError() << "invalid array element type";
1188
1189 return success();
1190}
1191
1192emitc::ArrayType
1193emitc::ArrayType::cloneWith(std::optional<ArrayRef<int64_t>> shape,
1194 Type elementType) const {
1195 if (!shape)
1196 return emitc::ArrayType::get(getShape(), elementType);
1197 return emitc::ArrayType::get(*shape, elementType);
1198}
1199
1200//===----------------------------------------------------------------------===//
1201// LValueType
1202//===----------------------------------------------------------------------===//
1203
1204LogicalResult mlir::emitc::LValueType::verify(
1206 mlir::Type value) {
1207 // Check that the wrapped type is valid. This especially forbids nested
1208 // lvalue types.
1209 if (!isSupportedEmitCType(value))
1210 return emitError()
1211 << "!emitc.lvalue must wrap supported emitc type, but got " << value;
1212
1213 if (llvm::isa<emitc::ArrayType>(value))
1214 return emitError() << "!emitc.lvalue cannot wrap !emitc.array type";
1215
1216 return success();
1217}
1218
1219//===----------------------------------------------------------------------===//
1220// OpaqueType
1221//===----------------------------------------------------------------------===//
1222
1223LogicalResult mlir::emitc::OpaqueType::verify(
1225 llvm::StringRef value) {
1226 if (value.empty()) {
1227 return emitError() << "expected non empty string in !emitc.opaque type";
1228 }
1229 if (value.back() == '*') {
1230 return emitError() << "pointer not allowed as outer type with "
1231 "!emitc.opaque, use !emitc.ptr instead";
1232 }
1233 return success();
1234}
1235
1236//===----------------------------------------------------------------------===//
1237// PointerType
1238//===----------------------------------------------------------------------===//
1239
1240LogicalResult mlir::emitc::PointerType::verify(
1242 if (llvm::isa<emitc::LValueType>(value))
1243 return emitError() << "pointers to lvalues are not allowed";
1244
1245 return success();
1246}
1247
1248//===----------------------------------------------------------------------===//
1249// GlobalOp
1250//===----------------------------------------------------------------------===//
1252 TypeAttr type,
1253 Attribute initialValue) {
1254 p << type;
1255 if (initialValue) {
1256 p << " = ";
1257 p.printAttributeWithoutType(initialValue);
1258 }
1259}
1260
1262 if (auto array = llvm::dyn_cast<ArrayType>(type))
1263 return RankedTensorType::get(array.getShape(), array.getElementType());
1264 return type;
1265}
1266
1267static ParseResult
1269 Attribute &initialValue) {
1270 Type type;
1271 if (parser.parseType(type))
1272 return failure();
1273
1274 typeAttr = TypeAttr::get(type);
1275
1276 if (parser.parseOptionalEqual())
1277 return success();
1278
1279 if (parser.parseAttribute(initialValue, getInitializerTypeForGlobal(type)))
1280 return failure();
1281
1282 if (!llvm::isa<ElementsAttr, IntegerAttr, FloatAttr, emitc::OpaqueAttr>(
1283 initialValue))
1284 return parser.emitError(parser.getNameLoc())
1285 << "initial value should be a integer, float, elements or opaque "
1286 "attribute";
1287 return success();
1288}
1289
1290LogicalResult GlobalOp::verify() {
1291 if (!isSupportedEmitCType(getType())) {
1292 return emitOpError("expected valid emitc type");
1293 }
1294 if (getInitialValue().has_value()) {
1295 Attribute initValue = getInitialValue().value();
1296 // Check that the type of the initial value is compatible with the type of
1297 // the global variable.
1298 if (auto elementsAttr = llvm::dyn_cast<ElementsAttr>(initValue)) {
1299 auto arrayType = llvm::dyn_cast<ArrayType>(getType());
1300 if (!arrayType)
1301 return emitOpError("expected array type, but got ") << getType();
1302
1303 Type initType = elementsAttr.getType();
1305 if (initType != tensorType) {
1306 return emitOpError("initial value expected to be of type ")
1307 << getType() << ", but was of type " << initType;
1308 }
1309 } else if (auto intAttr = dyn_cast<IntegerAttr>(initValue)) {
1310 if (intAttr.getType() != getType()) {
1311 return emitOpError("initial value expected to be of type ")
1312 << getType() << ", but was of type " << intAttr.getType();
1313 }
1314 } else if (auto floatAttr = dyn_cast<FloatAttr>(initValue)) {
1315 if (floatAttr.getType() != getType()) {
1316 return emitOpError("initial value expected to be of type ")
1317 << getType() << ", but was of type " << floatAttr.getType();
1318 }
1319 } else if (!isa<emitc::OpaqueAttr>(initValue)) {
1320 return emitOpError("initial value should be a integer, float, elements "
1321 "or opaque attribute, but got ")
1322 << initValue;
1323 }
1324 }
1325 if (getStaticSpecifier() && getExternSpecifier()) {
1326 return emitOpError("cannot have both static and extern specifiers");
1327 }
1328 return success();
1329}
1330
1331//===----------------------------------------------------------------------===//
1332// GetGlobalOp
1333//===----------------------------------------------------------------------===//
1334
1335LogicalResult
1336GetGlobalOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1337 // Verify that the type matches the type of the global variable.
1338 auto global =
1339 symbolTable.lookupNearestSymbolFrom<GlobalOp>(*this, getNameAttr());
1340 if (!global)
1341 return emitOpError("'")
1342 << getName() << "' does not reference a valid emitc.global";
1343
1344 Type resultType = getResult().getType();
1345 Type globalType = global.getType();
1346
1347 // global has array type
1348 if (llvm::isa<ArrayType>(globalType)) {
1349 if (globalType != resultType)
1350 return emitOpError("on array type expects result type ")
1351 << resultType << " to match type " << globalType
1352 << " of the global @" << getName();
1353 return success();
1354 }
1355
1356 // global has non-array type
1357 auto lvalueType = dyn_cast<LValueType>(resultType);
1358 if (!lvalueType)
1359 return emitOpError("on non-array type expects result type to be an "
1360 "lvalue type for the global @")
1361 << getName();
1362 if (lvalueType.getValueType() != globalType)
1363 return emitOpError("on non-array type expects result inner type ")
1364 << lvalueType.getValueType() << " to match type " << globalType
1365 << " of the global @" << getName();
1366 return success();
1367}
1368
1369//===----------------------------------------------------------------------===//
1370// SwitchOp
1371//===----------------------------------------------------------------------===//
1372
1373/// Parse the case regions and values.
1374static ParseResult
1376 SmallVectorImpl<std::unique_ptr<Region>> &caseRegions) {
1377 SmallVector<int64_t> caseValues;
1378 while (succeeded(parser.parseOptionalKeyword("case"))) {
1379 int64_t value;
1380 Region &region = *caseRegions.emplace_back(std::make_unique<Region>());
1381 if (parser.parseInteger(value) ||
1382 parser.parseRegion(region, /*arguments=*/{}))
1383 return failure();
1384 caseValues.push_back(value);
1385 }
1386 cases = parser.getBuilder().getDenseI64ArrayAttr(caseValues);
1387 return success();
1388}
1389
1390/// Print the case regions and values.
1392 DenseI64ArrayAttr cases, RegionRange caseRegions) {
1393 for (auto [value, region] : llvm::zip(cases.asArrayRef(), caseRegions)) {
1394 p.printNewline();
1395 p << "case " << value << ' ';
1396 p.printRegion(*region, /*printEntryBlockArgs=*/false);
1397 }
1398}
1399
1400static LogicalResult verifyRegion(emitc::SwitchOp op, Region &region,
1401 const Twine &name) {
1402 auto yield = dyn_cast<emitc::YieldOp>(region.front().back());
1403 if (!yield)
1404 return op.emitOpError("expected region to end with emitc.yield, but got ")
1405 << region.front().back().getName();
1406
1407 if (yield.getNumOperands() != 0) {
1408 return (op.emitOpError("expected each region to return ")
1409 << "0 values, but " << name << " returns "
1410 << yield.getNumOperands())
1411 .attachNote(yield.getLoc())
1412 << "see yield operation here";
1413 }
1414
1415 return success();
1416}
1417
1418LogicalResult emitc::SwitchOp::verify() {
1419 if (!isIntegerIndexOrOpaqueType(getArg().getType()))
1420 return emitOpError("unsupported type ") << getArg().getType();
1421
1422 if (getCases().size() != getCaseRegions().size()) {
1423 return emitOpError("has ")
1424 << getCaseRegions().size() << " case regions but "
1425 << getCases().size() << " case values";
1426 }
1427
1428 DenseSet<int64_t> valueSet;
1429 for (int64_t value : getCases())
1430 if (!valueSet.insert(value).second)
1431 return emitOpError("has duplicate case value: ") << value;
1432
1433 if (failed(verifyRegion(*this, getDefaultRegion(), "default region")))
1434 return failure();
1435
1436 for (auto [idx, caseRegion] : llvm::enumerate(getCaseRegions()))
1437 if (failed(verifyRegion(*this, caseRegion, "case region #" + Twine(idx))))
1438 return failure();
1439
1440 return success();
1441}
1442
1443unsigned emitc::SwitchOp::getNumCases() { return getCases().size(); }
1444
1445Block &emitc::SwitchOp::getDefaultBlock() { return getDefaultRegion().front(); }
1446
1447Block &emitc::SwitchOp::getCaseBlock(unsigned idx) {
1448 assert(idx < getNumCases() && "case index out-of-bounds");
1449 return getCaseRegions()[idx].front();
1450}
1451
1452void SwitchOp::getSuccessorRegions(
1454 llvm::append_range(successors, getRegions());
1455}
1456
1457void SwitchOp::getEntrySuccessorRegions(
1458 ArrayRef<Attribute> operands,
1460 FoldAdaptor adaptor(operands, *this);
1461
1462 // If a constant was not provided, all regions are possible successors.
1463 auto arg = dyn_cast_or_null<IntegerAttr>(adaptor.getArg());
1464 if (!arg) {
1465 llvm::append_range(successors, getRegions());
1466 return;
1467 }
1468
1469 // Otherwise, try to find a case with a matching value. If not, the
1470 // default region is the only successor.
1471 for (auto [caseValue, caseRegion] : llvm::zip(getCases(), getCaseRegions())) {
1472 if (caseValue == arg.getInt()) {
1473 successors.emplace_back(&caseRegion);
1474 return;
1475 }
1476 }
1477 successors.emplace_back(&getDefaultRegion());
1478}
1479
1480void SwitchOp::getRegionInvocationBounds(
1482 auto operandValue = llvm::dyn_cast_or_null<IntegerAttr>(operands.front());
1483 if (!operandValue) {
1484 // All regions are invoked at most once.
1485 bounds.append(getNumRegions(), InvocationBounds(/*lb=*/0, /*ub=*/1));
1486 return;
1487 }
1488
1489 unsigned liveIndex = getNumRegions() - 1;
1490 const auto *iteratorToInt = llvm::find(getCases(), operandValue.getInt());
1491
1492 liveIndex = iteratorToInt != getCases().end()
1493 ? std::distance(getCases().begin(), iteratorToInt)
1494 : liveIndex;
1495
1496 for (unsigned regIndex = 0, regNum = getNumRegions(); regIndex < regNum;
1497 ++regIndex)
1498 bounds.emplace_back(/*lb=*/0, /*ub=*/regIndex == liveIndex);
1499}
1500
1501//===----------------------------------------------------------------------===//
1502// FileOp
1503//===----------------------------------------------------------------------===//
1504void FileOp::build(OpBuilder &builder, OperationState &state, StringRef id) {
1505 state.addRegion()->emplaceBlock();
1506 state.attributes.push_back(
1507 builder.getNamedAttr("id", builder.getStringAttr(id)));
1508}
1509
1510//===----------------------------------------------------------------------===//
1511// FieldOp
1512//===----------------------------------------------------------------------===//
1513
1515 TypeAttr type,
1516 Attribute initialValue) {
1517 p << type;
1518 if (initialValue) {
1519 p << " = ";
1520 p.printAttributeWithoutType(initialValue);
1521 }
1522}
1523
1525 if (auto array = llvm::dyn_cast<ArrayType>(type))
1526 return RankedTensorType::get(array.getShape(), array.getElementType());
1527 return type;
1528}
1529
1530static ParseResult
1532 Attribute &initialValue) {
1533 Type type;
1534 if (parser.parseType(type))
1535 return failure();
1536
1537 typeAttr = TypeAttr::get(type);
1538
1539 if (parser.parseOptionalEqual())
1540 return success();
1541
1542 if (parser.parseAttribute(initialValue, getInitializerTypeForField(type)))
1543 return failure();
1544
1545 if (!llvm::isa<ElementsAttr, IntegerAttr, FloatAttr, emitc::OpaqueAttr>(
1546 initialValue))
1547 return parser.emitError(parser.getNameLoc())
1548 << "initial value should be a integer, float, elements or opaque "
1549 "attribute";
1550 return success();
1551}
1552
1553LogicalResult FieldOp::verify() {
1555 return emitOpError("expected valid emitc type");
1556
1557 Operation *parentOp = getOperation()->getParentOp();
1558 if (!parentOp || !isa<emitc::ClassOp>(parentOp))
1559 return emitOpError("field must be nested within an emitc.class operation");
1560
1561 StringAttr symName = getSymNameAttr();
1562 if (!symName || symName.getValue().empty())
1563 return emitOpError("field must have a non-empty symbol name");
1564
1565 return success();
1566}
1567
1568//===----------------------------------------------------------------------===//
1569// GetFieldOp
1570//===----------------------------------------------------------------------===//
1571
1572LogicalResult GetFieldOp::verify() {
1573 auto parentClassOp = getOperation()->getParentOfType<emitc::ClassOp>();
1574 if (!parentClassOp.getOperation())
1575 return emitOpError(" must be nested within an emitc.class operation");
1576
1577 return success();
1578}
1579
1580LogicalResult GetFieldOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1581 mlir::FlatSymbolRefAttr fieldNameAttr = getFieldNameAttr();
1582 FieldOp fieldOp =
1583 symbolTable.lookupNearestSymbolFrom<FieldOp>(*this, fieldNameAttr);
1584 if (!fieldOp)
1585 return emitOpError("field '")
1586 << fieldNameAttr << "' not found in the class";
1587
1588 Type getFieldResultType = getResult().getType();
1589 Type fieldType = fieldOp.getType();
1590
1591 if (fieldType != getFieldResultType)
1592 return emitOpError("result type ")
1593 << getFieldResultType << " does not match field '" << fieldNameAttr
1594 << "' type " << fieldType;
1595
1596 return success();
1597}
1598
1599//===----------------------------------------------------------------------===//
1600// DoOp
1601//===----------------------------------------------------------------------===//
1602
1603void DoOp::print(OpAsmPrinter &p) {
1604 p << ' ';
1605 p.printRegion(getBodyRegion(), /*printEntryBlockArgs=*/false);
1606 p << " while ";
1607 p.printRegion(getConditionRegion());
1608 p.printOptionalAttrDictWithKeyword(getOperation()->getAttrs());
1609}
1610
1611LogicalResult emitc::DoOp::verify() {
1612 Block &condBlock = getConditionRegion().front();
1613
1614 if (condBlock.getOperations().size() != 2)
1615 return emitOpError(
1616 "condition region must contain exactly two operations: "
1617 "'emitc.expression' followed by 'emitc.yield', but found ")
1618 << condBlock.getOperations().size() << " operations";
1619
1620 Operation &first = condBlock.front();
1621 auto exprOp = dyn_cast<emitc::ExpressionOp>(first);
1622 if (!exprOp)
1623 return emitOpError("expected first op in condition region to be "
1624 "'emitc.expression', but got ")
1625 << first.getName();
1626
1627 if (!exprOp.getResult().getType().isInteger(1))
1628 return emitOpError("emitc.expression in condition region must return "
1629 "'i1', but returns ")
1630 << exprOp.getResult().getType();
1631
1632 Operation &last = condBlock.back();
1633 auto condYield = dyn_cast<emitc::YieldOp>(last);
1634 if (!condYield)
1635 return emitOpError("expected last op in condition region to be "
1636 "'emitc.yield', but got ")
1637 << last.getName();
1638
1639 if (condYield.getNumOperands() != 1)
1640 return emitOpError("expected condition region to return 1 value, but "
1641 "it returns ")
1642 << condYield.getNumOperands() << " values";
1643
1644 if (condYield.getOperand(0) != exprOp.getResult())
1645 return emitError("'emitc.yield' must return result of "
1646 "'emitc.expression' from this condition region");
1647
1648 Block &bodyBlock = getBodyRegion().front();
1649 if (bodyBlock.mightHaveTerminator())
1650 return emitOpError("body region must not contain terminator");
1651
1652 return success();
1653}
1654
1655ParseResult DoOp::parse(OpAsmParser &parser, OperationState &result) {
1656 Region *bodyRegion = result.addRegion();
1657 Region *condRegion = result.addRegion();
1658
1659 if (parser.parseRegion(*bodyRegion) || parser.parseKeyword("while") ||
1660 parser.parseRegion(*condRegion))
1661 return failure();
1662
1663 if (bodyRegion->empty())
1664 bodyRegion->emplaceBlock();
1665
1666 return parser.parseOptionalAttrDictWithKeyword(result.attributes);
1667}
1668
1669//===----------------------------------------------------------------------===//
1670// TableGen'd op method definitions
1671//===----------------------------------------------------------------------===//
1672
1673#include "mlir/Dialect/EmitC/IR/EmitCInterfaces.cpp.inc"
1674
1675#define GET_OP_CLASSES
1676#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:1400
static ParseResult parseEmitCGlobalOpTypeAndInitialValue(OpAsmParser &parser, TypeAttr &typeAttr, Attribute &initialValue)
Definition EmitC.cpp:1268
static Type getInitializerTypeForField(Type type)
Definition EmitC.cpp:1524
static ParseResult parseEmitCFieldOpTypeAndInitialValue(OpAsmParser &parser, TypeAttr &typeAttr, Attribute &initialValue)
Definition EmitC.cpp:1531
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:1251
static ParseResult parseSwitchCases(OpAsmParser &parser, DenseI64ArrayAttr &cases, SmallVectorImpl< std::unique_ptr< Region > > &caseRegions)
Parse the case regions and values.
Definition EmitC.cpp:1375
static void printEmitCFieldOpTypeAndInitialValue(OpAsmPrinter &p, FieldOp op, TypeAttr type, Attribute initialValue)
Definition EmitC.cpp:1514
static void printSwitchCases(OpAsmPrinter &p, Operation *op, DenseI64ArrayAttr cases, RegionRange caseRegions)
Print the case regions and values.
Definition EmitC.cpp:1391
static Type getInitializerTypeForGlobal(Type type)
Definition EmitC.cpp:1261
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:775
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)
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:129
OpListType & getOperations()
Definition Block.h:137
Operation & front()
Definition Block.h:153
Operation & back()
Definition Block.h:152
Operation * getTerminator()
Get the terminator operation of this block.
Definition Block.cpp:244
BlockArgument addArgument(Type type, Location loc)
Add one value to the argument list.
Definition Block.cpp:153
bool mightHaveTerminator()
Return "true" if this block might have a terminator.
Definition Block.cpp:250
iterator_range< iterator > without_terminator()
Return an iterator range over the operation within this block excluding the terminator operation at t...
Definition Block.h:212
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.
virtual void printNewline()=0
Print a newline and indent the printer to the start of the current operation.
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.
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 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:4804
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.