clang 22.0.0git
CIRDialect.cpp
Go to the documentation of this file.
1//===- CIRDialect.cpp - MLIR CIR ops implementation -----------------------===//
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//
9// This file implements the CIR dialect and its operations.
10//
11//===----------------------------------------------------------------------===//
12
14
18
19#include "mlir/IR/Attributes.h"
20#include "mlir/IR/DialectImplementation.h"
21#include "mlir/Interfaces/ControlFlowInterfaces.h"
22#include "mlir/Interfaces/FunctionImplementation.h"
23#include "mlir/Support/LLVM.h"
24
25#include "clang/CIR/Dialect/IR/CIROpsDialect.cpp.inc"
26#include "clang/CIR/Dialect/IR/CIROpsEnums.cpp.inc"
28#include "llvm/ADT/SetOperations.h"
29#include "llvm/ADT/SmallSet.h"
30#include "llvm/Support/LogicalResult.h"
31
32using namespace mlir;
33using namespace cir;
34
35//===----------------------------------------------------------------------===//
36// CIR Dialect
37//===----------------------------------------------------------------------===//
38namespace {
39struct CIROpAsmDialectInterface : public OpAsmDialectInterface {
40 using OpAsmDialectInterface::OpAsmDialectInterface;
41
42 AliasResult getAlias(Type type, raw_ostream &os) const final {
43 if (auto recordType = dyn_cast<cir::RecordType>(type)) {
44 StringAttr nameAttr = recordType.getName();
45 if (!nameAttr)
46 os << "rec_anon_" << recordType.getKindAsStr();
47 else
48 os << "rec_" << nameAttr.getValue();
49 return AliasResult::OverridableAlias;
50 }
51 if (auto intType = dyn_cast<cir::IntType>(type)) {
52 // We only provide alias for standard integer types (i.e. integer types
53 // whose width is a power of 2 and at least 8).
54 unsigned width = intType.getWidth();
55 if (width < 8 || !llvm::isPowerOf2_32(width))
56 return AliasResult::NoAlias;
57 os << intType.getAlias();
58 return AliasResult::OverridableAlias;
59 }
60 if (auto voidType = dyn_cast<cir::VoidType>(type)) {
61 os << voidType.getAlias();
62 return AliasResult::OverridableAlias;
63 }
64
65 return AliasResult::NoAlias;
66 }
67
68 AliasResult getAlias(Attribute attr, raw_ostream &os) const final {
69 if (auto boolAttr = mlir::dyn_cast<cir::BoolAttr>(attr)) {
70 os << (boolAttr.getValue() ? "true" : "false");
71 return AliasResult::FinalAlias;
72 }
73 if (auto bitfield = mlir::dyn_cast<cir::BitfieldInfoAttr>(attr)) {
74 os << "bfi_" << bitfield.getName().str();
75 return AliasResult::FinalAlias;
76 }
77 if (auto dynCastInfoAttr = mlir::dyn_cast<cir::DynamicCastInfoAttr>(attr)) {
78 os << dynCastInfoAttr.getAlias();
79 return AliasResult::FinalAlias;
80 }
81 return AliasResult::NoAlias;
82 }
83};
84} // namespace
85
86void cir::CIRDialect::initialize() {
87 registerTypes();
88 registerAttributes();
89 addOperations<
90#define GET_OP_LIST
91#include "clang/CIR/Dialect/IR/CIROps.cpp.inc"
92 >();
93 addInterfaces<CIROpAsmDialectInterface>();
94}
95
96Operation *cir::CIRDialect::materializeConstant(mlir::OpBuilder &builder,
97 mlir::Attribute value,
98 mlir::Type type,
99 mlir::Location loc) {
100 return cir::ConstantOp::create(builder, loc, type,
101 mlir::cast<mlir::TypedAttr>(value));
102}
103
104//===----------------------------------------------------------------------===//
105// Helpers
106//===----------------------------------------------------------------------===//
107
108// Parses one of the keywords provided in the list `keywords` and returns the
109// position of the parsed keyword in the list. If none of the keywords from the
110// list is parsed, returns -1.
111static int parseOptionalKeywordAlternative(AsmParser &parser,
112 ArrayRef<llvm::StringRef> keywords) {
113 for (auto en : llvm::enumerate(keywords)) {
114 if (succeeded(parser.parseOptionalKeyword(en.value())))
115 return en.index();
116 }
117 return -1;
118}
119
120namespace {
121template <typename Ty> struct EnumTraits {};
122
123#define REGISTER_ENUM_TYPE(Ty) \
124 template <> struct EnumTraits<cir::Ty> { \
125 static llvm::StringRef stringify(cir::Ty value) { \
126 return stringify##Ty(value); \
127 } \
128 static unsigned getMaxEnumVal() { return cir::getMaxEnumValFor##Ty(); } \
129 }
130
131REGISTER_ENUM_TYPE(GlobalLinkageKind);
132REGISTER_ENUM_TYPE(VisibilityKind);
133REGISTER_ENUM_TYPE(SideEffect);
134} // namespace
135
136/// Parse an enum from the keyword, or default to the provided default value.
137/// The return type is the enum type by default, unless overriden with the
138/// second template argument.
139template <typename EnumTy, typename RetTy = EnumTy>
140static RetTy parseOptionalCIRKeyword(AsmParser &parser, EnumTy defaultValue) {
142 for (unsigned i = 0, e = EnumTraits<EnumTy>::getMaxEnumVal(); i <= e; ++i)
143 names.push_back(EnumTraits<EnumTy>::stringify(static_cast<EnumTy>(i)));
144
145 int index = parseOptionalKeywordAlternative(parser, names);
146 if (index == -1)
147 return static_cast<RetTy>(defaultValue);
148 return static_cast<RetTy>(index);
149}
150
151/// Parse an enum from the keyword, return failure if the keyword is not found.
152template <typename EnumTy, typename RetTy = EnumTy>
153static ParseResult parseCIRKeyword(AsmParser &parser, RetTy &result) {
155 for (unsigned i = 0, e = EnumTraits<EnumTy>::getMaxEnumVal(); i <= e; ++i)
156 names.push_back(EnumTraits<EnumTy>::stringify(static_cast<EnumTy>(i)));
157
158 int index = parseOptionalKeywordAlternative(parser, names);
159 if (index == -1)
160 return failure();
161 result = static_cast<RetTy>(index);
162 return success();
163}
164
165// Check if a region's termination omission is valid and, if so, creates and
166// inserts the omitted terminator into the region.
167static LogicalResult ensureRegionTerm(OpAsmParser &parser, Region &region,
168 SMLoc errLoc) {
169 Location eLoc = parser.getEncodedSourceLoc(parser.getCurrentLocation());
170 OpBuilder builder(parser.getBuilder().getContext());
171
172 // Insert empty block in case the region is empty to ensure the terminator
173 // will be inserted
174 if (region.empty())
175 builder.createBlock(&region);
176
177 Block &block = region.back();
178 // Region is properly terminated: nothing to do.
179 if (!block.empty() && block.back().hasTrait<OpTrait::IsTerminator>())
180 return success();
181
182 // Check for invalid terminator omissions.
183 if (!region.hasOneBlock())
184 return parser.emitError(errLoc,
185 "multi-block region must not omit terminator");
186
187 // Terminator was omitted correctly: recreate it.
188 builder.setInsertionPointToEnd(&block);
189 cir::YieldOp::create(builder, eLoc);
190 return success();
191}
192
193// True if the region's terminator should be omitted.
194static bool omitRegionTerm(mlir::Region &r) {
195 const auto singleNonEmptyBlock = r.hasOneBlock() && !r.back().empty();
196 const auto yieldsNothing = [&r]() {
197 auto y = dyn_cast<cir::YieldOp>(r.back().getTerminator());
198 return y && y.getArgs().empty();
199 };
200 return singleNonEmptyBlock && yieldsNothing();
201}
202
203void printVisibilityAttr(OpAsmPrinter &printer,
204 cir::VisibilityAttr &visibility) {
205 switch (visibility.getValue()) {
206 case cir::VisibilityKind::Hidden:
207 printer << "hidden";
208 break;
209 case cir::VisibilityKind::Protected:
210 printer << "protected";
211 break;
212 case cir::VisibilityKind::Default:
213 break;
214 }
215}
216
217void parseVisibilityAttr(OpAsmParser &parser, cir::VisibilityAttr &visibility) {
218 cir::VisibilityKind visibilityKind =
219 parseOptionalCIRKeyword(parser, cir::VisibilityKind::Default);
220 visibility = cir::VisibilityAttr::get(parser.getContext(), visibilityKind);
221}
222
223//===----------------------------------------------------------------------===//
224// InlineKindAttr (FIXME: remove once FuncOp uses assembly format)
225//===----------------------------------------------------------------------===//
226
227ParseResult parseInlineKindAttr(OpAsmParser &parser,
228 cir::InlineKindAttr &inlineKindAttr) {
229 // Static list of possible inline kind keywords
230 static constexpr llvm::StringRef keywords[] = {"no_inline", "always_inline",
231 "inline_hint"};
232
233 // Parse the inline kind keyword (optional)
234 llvm::StringRef keyword;
235 if (parser.parseOptionalKeyword(&keyword, keywords).failed()) {
236 // Not an inline kind keyword, leave inlineKindAttr empty
237 return success();
238 }
239
240 // Parse the enum value from the keyword
241 auto inlineKindResult = ::cir::symbolizeEnum<::cir::InlineKind>(keyword);
242 if (!inlineKindResult) {
243 return parser.emitError(parser.getCurrentLocation(), "expected one of [")
244 << llvm::join(llvm::ArrayRef(keywords), ", ")
245 << "] for inlineKind, got: " << keyword;
246 }
247
248 inlineKindAttr =
249 ::cir::InlineKindAttr::get(parser.getContext(), *inlineKindResult);
250 return success();
251}
252
253void printInlineKindAttr(OpAsmPrinter &p, cir::InlineKindAttr inlineKindAttr) {
254 if (inlineKindAttr) {
255 p << " " << stringifyInlineKind(inlineKindAttr.getValue());
256 }
257}
258//===----------------------------------------------------------------------===//
259// CIR Custom Parsers/Printers
260//===----------------------------------------------------------------------===//
261
262static mlir::ParseResult parseOmittedTerminatorRegion(mlir::OpAsmParser &parser,
263 mlir::Region &region) {
264 auto regionLoc = parser.getCurrentLocation();
265 if (parser.parseRegion(region))
266 return failure();
267 if (ensureRegionTerm(parser, region, regionLoc).failed())
268 return failure();
269 return success();
270}
271
272static void printOmittedTerminatorRegion(mlir::OpAsmPrinter &printer,
273 cir::ScopeOp &op,
274 mlir::Region &region) {
275 printer.printRegion(region,
276 /*printEntryBlockArgs=*/false,
277 /*printBlockTerminators=*/!omitRegionTerm(region));
278}
279
280//===----------------------------------------------------------------------===//
281// AllocaOp
282//===----------------------------------------------------------------------===//
283
284void cir::AllocaOp::build(mlir::OpBuilder &odsBuilder,
285 mlir::OperationState &odsState, mlir::Type addr,
286 mlir::Type allocaType, llvm::StringRef name,
287 mlir::IntegerAttr alignment) {
288 odsState.addAttribute(getAllocaTypeAttrName(odsState.name),
289 mlir::TypeAttr::get(allocaType));
290 odsState.addAttribute(getNameAttrName(odsState.name),
291 odsBuilder.getStringAttr(name));
292 if (alignment) {
293 odsState.addAttribute(getAlignmentAttrName(odsState.name), alignment);
294 }
295 odsState.addTypes(addr);
296}
297
298//===----------------------------------------------------------------------===//
299// BreakOp
300//===----------------------------------------------------------------------===//
301
302LogicalResult cir::BreakOp::verify() {
304 if (!getOperation()->getParentOfType<LoopOpInterface>() &&
305 !getOperation()->getParentOfType<SwitchOp>())
306 return emitOpError("must be within a loop");
307 return success();
308}
309
310//===----------------------------------------------------------------------===//
311// ConditionOp
312//===----------------------------------------------------------------------===//
313
314//===----------------------------------
315// BranchOpTerminatorInterface Methods
316//===----------------------------------
317
318void cir::ConditionOp::getSuccessorRegions(
319 ArrayRef<Attribute> operands, SmallVectorImpl<RegionSuccessor> &regions) {
320 // TODO(cir): The condition value may be folded to a constant, narrowing
321 // down its list of possible successors.
322
323 // Parent is a loop: condition may branch to the body or to the parent op.
324 if (auto loopOp = dyn_cast<LoopOpInterface>(getOperation()->getParentOp())) {
325 regions.emplace_back(&loopOp.getBody(), loopOp.getBody().getArguments());
326 regions.emplace_back(getOperation(), loopOp->getResults());
327 }
328
329 // Parent is an await: condition may branch to resume or suspend regions.
330 auto await = cast<AwaitOp>(getOperation()->getParentOp());
331 regions.emplace_back(&await.getResume(), await.getResume().getArguments());
332 regions.emplace_back(&await.getSuspend(), await.getSuspend().getArguments());
333}
334
335MutableOperandRange
336cir::ConditionOp::getMutableSuccessorOperands(RegionSuccessor point) {
337 // No values are yielded to the successor region.
338 return MutableOperandRange(getOperation(), 0, 0);
339}
340
341LogicalResult cir::ConditionOp::verify() {
342 if (!isa<LoopOpInterface, AwaitOp>(getOperation()->getParentOp()))
343 return emitOpError("condition must be within a conditional region");
344 return success();
345}
346
347//===----------------------------------------------------------------------===//
348// ConstantOp
349//===----------------------------------------------------------------------===//
350
351static LogicalResult checkConstantTypes(mlir::Operation *op, mlir::Type opType,
352 mlir::Attribute attrType) {
353 if (isa<cir::ConstPtrAttr>(attrType)) {
354 if (!mlir::isa<cir::PointerType>(opType))
355 return op->emitOpError(
356 "pointer constant initializing a non-pointer type");
357 return success();
358 }
359
360 if (isa<cir::DataMemberAttr>(attrType)) {
361 // More detailed type verifications are already done in
362 // DataMemberAttr::verify. Don't need to repeat here.
363 return success();
364 }
365
366 if (isa<cir::ZeroAttr>(attrType)) {
367 if (isa<cir::RecordType, cir::ArrayType, cir::VectorType, cir::ComplexType>(
368 opType))
369 return success();
370 return op->emitOpError(
371 "zero expects struct, array, vector, or complex type");
372 }
373
374 if (mlir::isa<cir::UndefAttr>(attrType)) {
375 if (!mlir::isa<cir::VoidType>(opType))
376 return success();
377 return op->emitOpError("undef expects non-void type");
378 }
379
380 if (mlir::isa<cir::BoolAttr>(attrType)) {
381 if (!mlir::isa<cir::BoolType>(opType))
382 return op->emitOpError("result type (")
383 << opType << ") must be '!cir.bool' for '" << attrType << "'";
384 return success();
385 }
386
387 if (mlir::isa<cir::IntAttr, cir::FPAttr>(attrType)) {
388 auto at = cast<TypedAttr>(attrType);
389 if (at.getType() != opType) {
390 return op->emitOpError("result type (")
391 << opType << ") does not match value type (" << at.getType()
392 << ")";
393 }
394 return success();
395 }
396
397 if (mlir::isa<cir::ConstArrayAttr, cir::ConstVectorAttr,
398 cir::ConstComplexAttr, cir::ConstRecordAttr,
399 cir::GlobalViewAttr, cir::PoisonAttr, cir::TypeInfoAttr,
400 cir::VTableAttr>(attrType))
401 return success();
402
403 assert(isa<TypedAttr>(attrType) && "What else could we be looking at here?");
404 return op->emitOpError("global with type ")
405 << cast<TypedAttr>(attrType).getType() << " not yet supported";
406}
407
408LogicalResult cir::ConstantOp::verify() {
409 // ODS already generates checks to make sure the result type is valid. We just
410 // need to additionally check that the value's attribute type is consistent
411 // with the result type.
412 return checkConstantTypes(getOperation(), getType(), getValue());
413}
414
415OpFoldResult cir::ConstantOp::fold(FoldAdaptor /*adaptor*/) {
416 return getValue();
417}
418
419//===----------------------------------------------------------------------===//
420// ContinueOp
421//===----------------------------------------------------------------------===//
422
423LogicalResult cir::ContinueOp::verify() {
424 if (!getOperation()->getParentOfType<LoopOpInterface>())
425 return emitOpError("must be within a loop");
426 return success();
427}
428
429//===----------------------------------------------------------------------===//
430// CastOp
431//===----------------------------------------------------------------------===//
432
433LogicalResult cir::CastOp::verify() {
434 mlir::Type resType = getType();
435 mlir::Type srcType = getSrc().getType();
436
437 // Verify address space casts for pointer types. given that
438 // casts for within a different address space are illegal.
439 auto srcPtrTy = mlir::dyn_cast<cir::PointerType>(srcType);
440 auto resPtrTy = mlir::dyn_cast<cir::PointerType>(resType);
441 if (srcPtrTy && resPtrTy && (getKind() != cir::CastKind::address_space))
442 if (srcPtrTy.getAddrSpace() != resPtrTy.getAddrSpace()) {
443 return emitOpError() << "result type address space does not match the "
444 "address space of the operand";
445 }
446
447 if (mlir::isa<cir::VectorType>(srcType) &&
448 mlir::isa<cir::VectorType>(resType)) {
449 // Use the element type of the vector to verify the cast kind. (Except for
450 // bitcast, see below.)
451 srcType = mlir::dyn_cast<cir::VectorType>(srcType).getElementType();
452 resType = mlir::dyn_cast<cir::VectorType>(resType).getElementType();
453 }
454
455 switch (getKind()) {
456 case cir::CastKind::int_to_bool: {
457 if (!mlir::isa<cir::BoolType>(resType))
458 return emitOpError() << "requires !cir.bool type for result";
459 if (!mlir::isa<cir::IntType>(srcType))
460 return emitOpError() << "requires !cir.int type for source";
461 return success();
462 }
463 case cir::CastKind::ptr_to_bool: {
464 if (!mlir::isa<cir::BoolType>(resType))
465 return emitOpError() << "requires !cir.bool type for result";
466 if (!mlir::isa<cir::PointerType>(srcType))
467 return emitOpError() << "requires !cir.ptr type for source";
468 return success();
469 }
470 case cir::CastKind::integral: {
471 if (!mlir::isa<cir::IntType>(resType))
472 return emitOpError() << "requires !cir.int type for result";
473 if (!mlir::isa<cir::IntType>(srcType))
474 return emitOpError() << "requires !cir.int type for source";
475 return success();
476 }
477 case cir::CastKind::array_to_ptrdecay: {
478 const auto arrayPtrTy = mlir::dyn_cast<cir::PointerType>(srcType);
479 const auto flatPtrTy = mlir::dyn_cast<cir::PointerType>(resType);
480 if (!arrayPtrTy || !flatPtrTy)
481 return emitOpError() << "requires !cir.ptr type for source and result";
482
483 // TODO(CIR): Make sure the AddrSpace of both types are equals
484 return success();
485 }
486 case cir::CastKind::bitcast: {
487 // Handle the pointer types first.
488 auto srcPtrTy = mlir::dyn_cast<cir::PointerType>(srcType);
489 auto resPtrTy = mlir::dyn_cast<cir::PointerType>(resType);
490
491 if (srcPtrTy && resPtrTy) {
492 return success();
493 }
494
495 return success();
496 }
497 case cir::CastKind::floating: {
498 if (!mlir::isa<cir::FPTypeInterface>(srcType) ||
499 !mlir::isa<cir::FPTypeInterface>(resType))
500 return emitOpError() << "requires !cir.float type for source and result";
501 return success();
502 }
503 case cir::CastKind::float_to_int: {
504 if (!mlir::isa<cir::FPTypeInterface>(srcType))
505 return emitOpError() << "requires !cir.float type for source";
506 if (!mlir::dyn_cast<cir::IntType>(resType))
507 return emitOpError() << "requires !cir.int type for result";
508 return success();
509 }
510 case cir::CastKind::int_to_ptr: {
511 if (!mlir::dyn_cast<cir::IntType>(srcType))
512 return emitOpError() << "requires !cir.int type for source";
513 if (!mlir::dyn_cast<cir::PointerType>(resType))
514 return emitOpError() << "requires !cir.ptr type for result";
515 return success();
516 }
517 case cir::CastKind::ptr_to_int: {
518 if (!mlir::dyn_cast<cir::PointerType>(srcType))
519 return emitOpError() << "requires !cir.ptr type for source";
520 if (!mlir::dyn_cast<cir::IntType>(resType))
521 return emitOpError() << "requires !cir.int type for result";
522 return success();
523 }
524 case cir::CastKind::float_to_bool: {
525 if (!mlir::isa<cir::FPTypeInterface>(srcType))
526 return emitOpError() << "requires !cir.float type for source";
527 if (!mlir::isa<cir::BoolType>(resType))
528 return emitOpError() << "requires !cir.bool type for result";
529 return success();
530 }
531 case cir::CastKind::bool_to_int: {
532 if (!mlir::isa<cir::BoolType>(srcType))
533 return emitOpError() << "requires !cir.bool type for source";
534 if (!mlir::isa<cir::IntType>(resType))
535 return emitOpError() << "requires !cir.int type for result";
536 return success();
537 }
538 case cir::CastKind::int_to_float: {
539 if (!mlir::isa<cir::IntType>(srcType))
540 return emitOpError() << "requires !cir.int type for source";
541 if (!mlir::isa<cir::FPTypeInterface>(resType))
542 return emitOpError() << "requires !cir.float type for result";
543 return success();
544 }
545 case cir::CastKind::bool_to_float: {
546 if (!mlir::isa<cir::BoolType>(srcType))
547 return emitOpError() << "requires !cir.bool type for source";
548 if (!mlir::isa<cir::FPTypeInterface>(resType))
549 return emitOpError() << "requires !cir.float type for result";
550 return success();
551 }
552 case cir::CastKind::address_space: {
553 auto srcPtrTy = mlir::dyn_cast<cir::PointerType>(srcType);
554 auto resPtrTy = mlir::dyn_cast<cir::PointerType>(resType);
555 if (!srcPtrTy || !resPtrTy)
556 return emitOpError() << "requires !cir.ptr type for source and result";
557 if (srcPtrTy.getPointee() != resPtrTy.getPointee())
558 return emitOpError() << "requires two types differ in addrspace only";
559 return success();
560 }
561 case cir::CastKind::float_to_complex: {
562 if (!mlir::isa<cir::FPTypeInterface>(srcType))
563 return emitOpError() << "requires !cir.float type for source";
564 auto resComplexTy = mlir::dyn_cast<cir::ComplexType>(resType);
565 if (!resComplexTy)
566 return emitOpError() << "requires !cir.complex type for result";
567 if (srcType != resComplexTy.getElementType())
568 return emitOpError() << "requires source type match result element type";
569 return success();
570 }
571 case cir::CastKind::int_to_complex: {
572 if (!mlir::isa<cir::IntType>(srcType))
573 return emitOpError() << "requires !cir.int type for source";
574 auto resComplexTy = mlir::dyn_cast<cir::ComplexType>(resType);
575 if (!resComplexTy)
576 return emitOpError() << "requires !cir.complex type for result";
577 if (srcType != resComplexTy.getElementType())
578 return emitOpError() << "requires source type match result element type";
579 return success();
580 }
581 case cir::CastKind::float_complex_to_real: {
582 auto srcComplexTy = mlir::dyn_cast<cir::ComplexType>(srcType);
583 if (!srcComplexTy)
584 return emitOpError() << "requires !cir.complex type for source";
585 if (!mlir::isa<cir::FPTypeInterface>(resType))
586 return emitOpError() << "requires !cir.float type for result";
587 if (srcComplexTy.getElementType() != resType)
588 return emitOpError() << "requires source element type match result type";
589 return success();
590 }
591 case cir::CastKind::int_complex_to_real: {
592 auto srcComplexTy = mlir::dyn_cast<cir::ComplexType>(srcType);
593 if (!srcComplexTy)
594 return emitOpError() << "requires !cir.complex type for source";
595 if (!mlir::isa<cir::IntType>(resType))
596 return emitOpError() << "requires !cir.int type for result";
597 if (srcComplexTy.getElementType() != resType)
598 return emitOpError() << "requires source element type match result type";
599 return success();
600 }
601 case cir::CastKind::float_complex_to_bool: {
602 auto srcComplexTy = mlir::dyn_cast<cir::ComplexType>(srcType);
603 if (!srcComplexTy || !srcComplexTy.isFloatingPointComplex())
604 return emitOpError()
605 << "requires floating point !cir.complex type for source";
606 if (!mlir::isa<cir::BoolType>(resType))
607 return emitOpError() << "requires !cir.bool type for result";
608 return success();
609 }
610 case cir::CastKind::int_complex_to_bool: {
611 auto srcComplexTy = mlir::dyn_cast<cir::ComplexType>(srcType);
612 if (!srcComplexTy || !srcComplexTy.isIntegerComplex())
613 return emitOpError()
614 << "requires floating point !cir.complex type for source";
615 if (!mlir::isa<cir::BoolType>(resType))
616 return emitOpError() << "requires !cir.bool type for result";
617 return success();
618 }
619 case cir::CastKind::float_complex: {
620 auto srcComplexTy = mlir::dyn_cast<cir::ComplexType>(srcType);
621 if (!srcComplexTy || !srcComplexTy.isFloatingPointComplex())
622 return emitOpError()
623 << "requires floating point !cir.complex type for source";
624 auto resComplexTy = mlir::dyn_cast<cir::ComplexType>(resType);
625 if (!resComplexTy || !resComplexTy.isFloatingPointComplex())
626 return emitOpError()
627 << "requires floating point !cir.complex type for result";
628 return success();
629 }
630 case cir::CastKind::float_complex_to_int_complex: {
631 auto srcComplexTy = mlir::dyn_cast<cir::ComplexType>(srcType);
632 if (!srcComplexTy || !srcComplexTy.isFloatingPointComplex())
633 return emitOpError()
634 << "requires floating point !cir.complex type for source";
635 auto resComplexTy = mlir::dyn_cast<cir::ComplexType>(resType);
636 if (!resComplexTy || !resComplexTy.isIntegerComplex())
637 return emitOpError() << "requires integer !cir.complex type for result";
638 return success();
639 }
640 case cir::CastKind::int_complex: {
641 auto srcComplexTy = mlir::dyn_cast<cir::ComplexType>(srcType);
642 if (!srcComplexTy || !srcComplexTy.isIntegerComplex())
643 return emitOpError() << "requires integer !cir.complex type for source";
644 auto resComplexTy = mlir::dyn_cast<cir::ComplexType>(resType);
645 if (!resComplexTy || !resComplexTy.isIntegerComplex())
646 return emitOpError() << "requires integer !cir.complex type for result";
647 return success();
648 }
649 case cir::CastKind::int_complex_to_float_complex: {
650 auto srcComplexTy = mlir::dyn_cast<cir::ComplexType>(srcType);
651 if (!srcComplexTy || !srcComplexTy.isIntegerComplex())
652 return emitOpError() << "requires integer !cir.complex type for source";
653 auto resComplexTy = mlir::dyn_cast<cir::ComplexType>(resType);
654 if (!resComplexTy || !resComplexTy.isFloatingPointComplex())
655 return emitOpError()
656 << "requires floating point !cir.complex type for result";
657 return success();
658 }
659 default:
660 llvm_unreachable("Unknown CastOp kind?");
661 }
662}
663
664static bool isIntOrBoolCast(cir::CastOp op) {
665 auto kind = op.getKind();
666 return kind == cir::CastKind::bool_to_int ||
667 kind == cir::CastKind::int_to_bool || kind == cir::CastKind::integral;
668}
669
670static Value tryFoldCastChain(cir::CastOp op) {
671 cir::CastOp head = op, tail = op;
672
673 while (op) {
674 if (!isIntOrBoolCast(op))
675 break;
676 head = op;
677 op = head.getSrc().getDefiningOp<cir::CastOp>();
678 }
679
680 if (head == tail)
681 return {};
682
683 // if bool_to_int -> ... -> int_to_bool: take the bool
684 // as we had it was before all casts
685 if (head.getKind() == cir::CastKind::bool_to_int &&
686 tail.getKind() == cir::CastKind::int_to_bool)
687 return head.getSrc();
688
689 // if int_to_bool -> ... -> int_to_bool: take the result
690 // of the first one, as no other casts (and ext casts as well)
691 // don't change the first result
692 if (head.getKind() == cir::CastKind::int_to_bool &&
693 tail.getKind() == cir::CastKind::int_to_bool)
694 return head.getResult();
695
696 return {};
697}
698
699OpFoldResult cir::CastOp::fold(FoldAdaptor adaptor) {
700 if (mlir::isa_and_present<cir::PoisonAttr>(adaptor.getSrc())) {
701 // Propagate poison value
702 return cir::PoisonAttr::get(getContext(), getType());
703 }
704
705 if (getSrc().getType() == getType()) {
706 switch (getKind()) {
707 case cir::CastKind::integral: {
709 auto foldOrder = getSrc().getDefiningOp()->fold(foldResults);
710 if (foldOrder.succeeded() && mlir::isa<mlir::Attribute>(foldResults[0]))
711 return mlir::cast<mlir::Attribute>(foldResults[0]);
712 return {};
713 }
714 case cir::CastKind::bitcast:
715 case cir::CastKind::address_space:
716 case cir::CastKind::float_complex:
717 case cir::CastKind::int_complex: {
718 return getSrc();
719 }
720 default:
721 return {};
722 }
723 }
724
725 // Handle cases where a chain of casts cancel out.
726 Value result = tryFoldCastChain(*this);
727 if (result)
728 return result;
729
730 // Handle simple constant casts.
731 if (auto srcConst = getSrc().getDefiningOp<cir::ConstantOp>()) {
732 switch (getKind()) {
733 case cir::CastKind::integral: {
734 mlir::Type srcTy = getSrc().getType();
735 // Don't try to fold vector casts for now.
736 assert(mlir::isa<cir::VectorType>(srcTy) ==
737 mlir::isa<cir::VectorType>(getType()));
738 if (mlir::isa<cir::VectorType>(srcTy))
739 break;
740
741 auto srcIntTy = mlir::cast<cir::IntType>(srcTy);
742 auto dstIntTy = mlir::cast<cir::IntType>(getType());
743 APInt newVal =
744 srcIntTy.isSigned()
745 ? srcConst.getIntValue().sextOrTrunc(dstIntTy.getWidth())
746 : srcConst.getIntValue().zextOrTrunc(dstIntTy.getWidth());
747 return cir::IntAttr::get(dstIntTy, newVal);
748 }
749 default:
750 break;
751 }
752 }
753 return {};
754}
755
756//===----------------------------------------------------------------------===//
757// CallOp
758//===----------------------------------------------------------------------===//
759
760mlir::OperandRange cir::CallOp::getArgOperands() {
761 if (isIndirect())
762 return getArgs().drop_front(1);
763 return getArgs();
764}
765
766mlir::MutableOperandRange cir::CallOp::getArgOperandsMutable() {
767 mlir::MutableOperandRange args = getArgsMutable();
768 if (isIndirect())
769 return args.slice(1, args.size() - 1);
770 return args;
771}
772
773mlir::Value cir::CallOp::getIndirectCall() {
774 assert(isIndirect());
775 return getOperand(0);
776}
777
778/// Return the operand at index 'i'.
779Value cir::CallOp::getArgOperand(unsigned i) {
780 if (isIndirect())
781 ++i;
782 return getOperand(i);
783}
784
785/// Return the number of operands.
786unsigned cir::CallOp::getNumArgOperands() {
787 if (isIndirect())
788 return this->getOperation()->getNumOperands() - 1;
789 return this->getOperation()->getNumOperands();
790}
791
792static mlir::ParseResult
793parseTryCallDestinations(mlir::OpAsmParser &parser,
794 mlir::OperationState &result) {
795 mlir::Block *normalDestSuccessor;
796 if (parser.parseSuccessor(normalDestSuccessor))
797 return mlir::failure();
798
799 if (parser.parseComma())
800 return mlir::failure();
801
802 mlir::Block *unwindDestSuccessor;
803 if (parser.parseSuccessor(unwindDestSuccessor))
804 return mlir::failure();
805
806 result.addSuccessors(normalDestSuccessor);
807 result.addSuccessors(unwindDestSuccessor);
808 return mlir::success();
809}
810
811static mlir::ParseResult parseCallCommon(mlir::OpAsmParser &parser,
812 mlir::OperationState &result,
813 bool hasDestinationBlocks = false) {
815 llvm::SMLoc opsLoc;
816 mlir::FlatSymbolRefAttr calleeAttr;
817 llvm::ArrayRef<mlir::Type> allResultTypes;
818
819 // If we cannot parse a string callee, it means this is an indirect call.
820 if (!parser
821 .parseOptionalAttribute(calleeAttr, CIRDialect::getCalleeAttrName(),
822 result.attributes)
823 .has_value()) {
824 OpAsmParser::UnresolvedOperand indirectVal;
825 // Do not resolve right now, since we need to figure out the type
826 if (parser.parseOperand(indirectVal).failed())
827 return failure();
828 ops.push_back(indirectVal);
829 }
830
831 if (parser.parseLParen())
832 return mlir::failure();
833
834 opsLoc = parser.getCurrentLocation();
835 if (parser.parseOperandList(ops))
836 return mlir::failure();
837 if (parser.parseRParen())
838 return mlir::failure();
839
840 if (hasDestinationBlocks &&
841 parseTryCallDestinations(parser, result).failed()) {
842 return ::mlir::failure();
843 }
844
845 if (parser.parseOptionalKeyword("nothrow").succeeded())
846 result.addAttribute(CIRDialect::getNoThrowAttrName(),
847 mlir::UnitAttr::get(parser.getContext()));
848
849 if (parser.parseOptionalKeyword("side_effect").succeeded()) {
850 if (parser.parseLParen().failed())
851 return failure();
852 cir::SideEffect sideEffect;
853 if (parseCIRKeyword<cir::SideEffect>(parser, sideEffect).failed())
854 return failure();
855 if (parser.parseRParen().failed())
856 return failure();
857 auto attr = cir::SideEffectAttr::get(parser.getContext(), sideEffect);
858 result.addAttribute(CIRDialect::getSideEffectAttrName(), attr);
859 }
860
861 if (parser.parseOptionalAttrDict(result.attributes))
862 return ::mlir::failure();
863
864 if (parser.parseColon())
865 return ::mlir::failure();
866
867 mlir::FunctionType opsFnTy;
868 if (parser.parseType(opsFnTy))
869 return mlir::failure();
870
871 allResultTypes = opsFnTy.getResults();
872 result.addTypes(allResultTypes);
873
874 if (parser.resolveOperands(ops, opsFnTy.getInputs(), opsLoc, result.operands))
875 return mlir::failure();
876
877 return mlir::success();
878}
879
880static void printCallCommon(mlir::Operation *op,
881 mlir::FlatSymbolRefAttr calleeSym,
882 mlir::Value indirectCallee,
883 mlir::OpAsmPrinter &printer, bool isNothrow,
884 cir::SideEffect sideEffect,
885 mlir::Block *normalDest = nullptr,
886 mlir::Block *unwindDest = nullptr) {
887 printer << ' ';
888
889 auto callLikeOp = mlir::cast<cir::CIRCallOpInterface>(op);
890 auto ops = callLikeOp.getArgOperands();
891
892 if (calleeSym) {
893 // Direct calls
894 printer.printAttributeWithoutType(calleeSym);
895 } else {
896 // Indirect calls
897 assert(indirectCallee);
898 printer << indirectCallee;
899 }
900
901 printer << "(" << ops << ")";
902
903 if (normalDest) {
904 assert(unwindDest && "expected two successors");
905 auto tryCall = cast<cir::TryCallOp>(op);
906 printer << ' ' << tryCall.getNormalDest();
907 printer << ",";
908 printer << ' ';
909 printer << tryCall.getUnwindDest();
910 }
911
912 if (isNothrow)
913 printer << " nothrow";
914
915 if (sideEffect != cir::SideEffect::All) {
916 printer << " side_effect(";
917 printer << stringifySideEffect(sideEffect);
918 printer << ")";
919 }
920
922 CIRDialect::getCalleeAttrName(), CIRDialect::getNoThrowAttrName(),
923 CIRDialect::getSideEffectAttrName(),
924 CIRDialect::getOperandSegmentSizesAttrName()};
925 printer.printOptionalAttrDict(op->getAttrs(), elidedAttrs);
926 printer << " : ";
927 printer.printFunctionalType(op->getOperands().getTypes(),
928 op->getResultTypes());
929}
930
931mlir::ParseResult cir::CallOp::parse(mlir::OpAsmParser &parser,
932 mlir::OperationState &result) {
933 return parseCallCommon(parser, result);
934}
935
936void cir::CallOp::print(mlir::OpAsmPrinter &p) {
937 mlir::Value indirectCallee = isIndirect() ? getIndirectCall() : nullptr;
938 cir::SideEffect sideEffect = getSideEffect();
939 printCallCommon(*this, getCalleeAttr(), indirectCallee, p, getNothrow(),
940 sideEffect);
941}
942
943static LogicalResult
944verifyCallCommInSymbolUses(mlir::Operation *op,
945 SymbolTableCollection &symbolTable) {
946 auto fnAttr =
947 op->getAttrOfType<FlatSymbolRefAttr>(CIRDialect::getCalleeAttrName());
948 if (!fnAttr) {
949 // This is an indirect call, thus we don't have to check the symbol uses.
950 return mlir::success();
951 }
952
953 auto fn = symbolTable.lookupNearestSymbolFrom<cir::FuncOp>(op, fnAttr);
954 if (!fn)
955 return op->emitOpError() << "'" << fnAttr.getValue()
956 << "' does not reference a valid function";
957
958 auto callIf = dyn_cast<cir::CIRCallOpInterface>(op);
959 assert(callIf && "expected CIR call interface to be always available");
960
961 // Verify that the operand and result types match the callee. Note that
962 // argument-checking is disabled for functions without a prototype.
963 auto fnType = fn.getFunctionType();
964 if (!fn.getNoProto()) {
965 unsigned numCallOperands = callIf.getNumArgOperands();
966 unsigned numFnOpOperands = fnType.getNumInputs();
967
968 if (!fnType.isVarArg() && numCallOperands != numFnOpOperands)
969 return op->emitOpError("incorrect number of operands for callee");
970 if (fnType.isVarArg() && numCallOperands < numFnOpOperands)
971 return op->emitOpError("too few operands for callee");
972
973 for (unsigned i = 0, e = numFnOpOperands; i != e; ++i)
974 if (callIf.getArgOperand(i).getType() != fnType.getInput(i))
975 return op->emitOpError("operand type mismatch: expected operand type ")
976 << fnType.getInput(i) << ", but provided "
977 << op->getOperand(i).getType() << " for operand number " << i;
978 }
979
981
982 // Void function must not return any results.
983 if (fnType.hasVoidReturn() && op->getNumResults() != 0)
984 return op->emitOpError("callee returns void but call has results");
985
986 // Non-void function calls must return exactly one result.
987 if (!fnType.hasVoidReturn() && op->getNumResults() != 1)
988 return op->emitOpError("incorrect number of results for callee");
989
990 // Parent function and return value types must match.
991 if (!fnType.hasVoidReturn() &&
992 op->getResultTypes().front() != fnType.getReturnType()) {
993 return op->emitOpError("result type mismatch: expected ")
994 << fnType.getReturnType() << ", but provided "
995 << op->getResult(0).getType();
996 }
997
998 return mlir::success();
999}
1000
1001LogicalResult
1002cir::CallOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1003 return verifyCallCommInSymbolUses(*this, symbolTable);
1004}
1005
1006//===----------------------------------------------------------------------===//
1007// TryCallOp
1008//===----------------------------------------------------------------------===//
1009
1010mlir::OperandRange cir::TryCallOp::getArgOperands() {
1011 if (isIndirect())
1012 return getArgs().drop_front(1);
1013 return getArgs();
1014}
1015
1016mlir::MutableOperandRange cir::TryCallOp::getArgOperandsMutable() {
1017 mlir::MutableOperandRange args = getArgsMutable();
1018 if (isIndirect())
1019 return args.slice(1, args.size() - 1);
1020 return args;
1021}
1022
1023mlir::Value cir::TryCallOp::getIndirectCall() {
1024 assert(isIndirect());
1025 return getOperand(0);
1026}
1027
1028/// Return the operand at index 'i'.
1029Value cir::TryCallOp::getArgOperand(unsigned i) {
1030 if (isIndirect())
1031 ++i;
1032 return getOperand(i);
1033}
1034
1035/// Return the number of operands.
1036unsigned cir::TryCallOp::getNumArgOperands() {
1037 if (isIndirect())
1038 return this->getOperation()->getNumOperands() - 1;
1039 return this->getOperation()->getNumOperands();
1040}
1041
1042LogicalResult
1043cir::TryCallOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1044 return verifyCallCommInSymbolUses(*this, symbolTable);
1045}
1046
1047mlir::ParseResult cir::TryCallOp::parse(mlir::OpAsmParser &parser,
1048 mlir::OperationState &result) {
1049 return parseCallCommon(parser, result, /*hasDestinationBlocks=*/true);
1050}
1051
1052void cir::TryCallOp::print(::mlir::OpAsmPrinter &p) {
1053 mlir::Value indirectCallee = isIndirect() ? getIndirectCall() : nullptr;
1054 cir::SideEffect sideEffect = getSideEffect();
1055 printCallCommon(*this, getCalleeAttr(), indirectCallee, p, getNothrow(),
1056 sideEffect, getNormalDest(), getUnwindDest());
1057}
1058
1059//===----------------------------------------------------------------------===//
1060// ReturnOp
1061//===----------------------------------------------------------------------===//
1062
1063static mlir::LogicalResult checkReturnAndFunction(cir::ReturnOp op,
1064 cir::FuncOp function) {
1065 // ReturnOps currently only have a single optional operand.
1066 if (op.getNumOperands() > 1)
1067 return op.emitOpError() << "expects at most 1 return operand";
1068
1069 // Ensure returned type matches the function signature.
1070 auto expectedTy = function.getFunctionType().getReturnType();
1071 auto actualTy =
1072 (op.getNumOperands() == 0 ? cir::VoidType::get(op.getContext())
1073 : op.getOperand(0).getType());
1074 if (actualTy != expectedTy)
1075 return op.emitOpError() << "returns " << actualTy
1076 << " but enclosing function returns " << expectedTy;
1077
1078 return mlir::success();
1079}
1080
1081mlir::LogicalResult cir::ReturnOp::verify() {
1082 // Returns can be present in multiple different scopes, get the
1083 // wrapping function and start from there.
1084 auto *fnOp = getOperation()->getParentOp();
1085 while (!isa<cir::FuncOp>(fnOp))
1086 fnOp = fnOp->getParentOp();
1087
1088 // Make sure return types match function return type.
1089 if (checkReturnAndFunction(*this, cast<cir::FuncOp>(fnOp)).failed())
1090 return failure();
1091
1092 return success();
1093}
1094
1095//===----------------------------------------------------------------------===//
1096// IfOp
1097//===----------------------------------------------------------------------===//
1098
1099ParseResult cir::IfOp::parse(OpAsmParser &parser, OperationState &result) {
1100 // create the regions for 'then'.
1101 result.regions.reserve(2);
1102 Region *thenRegion = result.addRegion();
1103 Region *elseRegion = result.addRegion();
1104
1105 mlir::Builder &builder = parser.getBuilder();
1106 OpAsmParser::UnresolvedOperand cond;
1107 Type boolType = cir::BoolType::get(builder.getContext());
1108
1109 if (parser.parseOperand(cond) ||
1110 parser.resolveOperand(cond, boolType, result.operands))
1111 return failure();
1112
1113 // Parse 'then' region.
1114 mlir::SMLoc parseThenLoc = parser.getCurrentLocation();
1115 if (parser.parseRegion(*thenRegion, /*arguments=*/{}, /*argTypes=*/{}))
1116 return failure();
1117
1118 if (ensureRegionTerm(parser, *thenRegion, parseThenLoc).failed())
1119 return failure();
1120
1121 // If we find an 'else' keyword, parse the 'else' region.
1122 if (!parser.parseOptionalKeyword("else")) {
1123 mlir::SMLoc parseElseLoc = parser.getCurrentLocation();
1124 if (parser.parseRegion(*elseRegion, /*arguments=*/{}, /*argTypes=*/{}))
1125 return failure();
1126 if (ensureRegionTerm(parser, *elseRegion, parseElseLoc).failed())
1127 return failure();
1128 }
1129
1130 // Parse the optional attribute list.
1131 if (parser.parseOptionalAttrDict(result.attributes))
1132 return failure();
1133 return success();
1134}
1135
1136void cir::IfOp::print(OpAsmPrinter &p) {
1137 p << " " << getCondition() << " ";
1138 mlir::Region &thenRegion = this->getThenRegion();
1139 p.printRegion(thenRegion,
1140 /*printEntryBlockArgs=*/false,
1141 /*printBlockTerminators=*/!omitRegionTerm(thenRegion));
1142
1143 // Print the 'else' regions if it exists and has a block.
1144 mlir::Region &elseRegion = this->getElseRegion();
1145 if (!elseRegion.empty()) {
1146 p << " else ";
1147 p.printRegion(elseRegion,
1148 /*printEntryBlockArgs=*/false,
1149 /*printBlockTerminators=*/!omitRegionTerm(elseRegion));
1150 }
1151
1152 p.printOptionalAttrDict(getOperation()->getAttrs());
1153}
1154
1155/// Default callback for IfOp builders.
1156void cir::buildTerminatedBody(OpBuilder &builder, Location loc) {
1157 // add cir.yield to end of the block
1158 cir::YieldOp::create(builder, loc);
1159}
1160
1161/// Given the region at `index`, or the parent operation if `index` is None,
1162/// return the successor regions. These are the regions that may be selected
1163/// during the flow of control. `operands` is a set of optional attributes that
1164/// correspond to a constant value for each operand, or null if that operand is
1165/// not a constant.
1166void cir::IfOp::getSuccessorRegions(mlir::RegionBranchPoint point,
1167 SmallVectorImpl<RegionSuccessor> &regions) {
1168 // The `then` and the `else` region branch back to the parent operation.
1169 if (!point.isParent()) {
1170 regions.push_back(
1171 RegionSuccessor(getOperation(), getOperation()->getResults()));
1172 return;
1173 }
1174
1175 // Don't consider the else region if it is empty.
1176 Region *elseRegion = &this->getElseRegion();
1177 if (elseRegion->empty())
1178 elseRegion = nullptr;
1179
1180 // If the condition isn't constant, both regions may be executed.
1181 regions.push_back(RegionSuccessor(&getThenRegion()));
1182 // If the else region does not exist, it is not a viable successor.
1183 if (elseRegion)
1184 regions.push_back(RegionSuccessor(elseRegion));
1185
1186 return;
1187}
1188
1189void cir::IfOp::build(OpBuilder &builder, OperationState &result, Value cond,
1190 bool withElseRegion, BuilderCallbackRef thenBuilder,
1191 BuilderCallbackRef elseBuilder) {
1192 assert(thenBuilder && "the builder callback for 'then' must be present");
1193 result.addOperands(cond);
1194
1195 OpBuilder::InsertionGuard guard(builder);
1196 Region *thenRegion = result.addRegion();
1197 builder.createBlock(thenRegion);
1198 thenBuilder(builder, result.location);
1199
1200 Region *elseRegion = result.addRegion();
1201 if (!withElseRegion)
1202 return;
1203
1204 builder.createBlock(elseRegion);
1205 elseBuilder(builder, result.location);
1206}
1207
1208//===----------------------------------------------------------------------===//
1209// ScopeOp
1210//===----------------------------------------------------------------------===//
1211
1212/// Given the region at `index`, or the parent operation if `index` is None,
1213/// return the successor regions. These are the regions that may be selected
1214/// during the flow of control. `operands` is a set of optional attributes
1215/// that correspond to a constant value for each operand, or null if that
1216/// operand is not a constant.
1217void cir::ScopeOp::getSuccessorRegions(
1218 mlir::RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &regions) {
1219 // The only region always branch back to the parent operation.
1220 if (!point.isParent()) {
1221 regions.push_back(RegionSuccessor(getOperation(), getODSResults(0)));
1222 return;
1223 }
1224
1225 // If the condition isn't constant, both regions may be executed.
1226 regions.push_back(RegionSuccessor(&getScopeRegion()));
1227}
1228
1229void cir::ScopeOp::build(
1230 OpBuilder &builder, OperationState &result,
1231 function_ref<void(OpBuilder &, Type &, Location)> scopeBuilder) {
1232 assert(scopeBuilder && "the builder callback for 'then' must be present");
1233
1234 OpBuilder::InsertionGuard guard(builder);
1235 Region *scopeRegion = result.addRegion();
1236 builder.createBlock(scopeRegion);
1238
1239 mlir::Type yieldTy;
1240 scopeBuilder(builder, yieldTy, result.location);
1241
1242 if (yieldTy)
1243 result.addTypes(TypeRange{yieldTy});
1244}
1245
1246void cir::ScopeOp::build(
1247 OpBuilder &builder, OperationState &result,
1248 function_ref<void(OpBuilder &, Location)> scopeBuilder) {
1249 assert(scopeBuilder && "the builder callback for 'then' must be present");
1250 OpBuilder::InsertionGuard guard(builder);
1251 Region *scopeRegion = result.addRegion();
1252 builder.createBlock(scopeRegion);
1254 scopeBuilder(builder, result.location);
1255}
1256
1257LogicalResult cir::ScopeOp::verify() {
1258 if (getRegion().empty()) {
1259 return emitOpError() << "cir.scope must not be empty since it should "
1260 "include at least an implicit cir.yield ";
1261 }
1262
1263 mlir::Block &lastBlock = getRegion().back();
1264 if (lastBlock.empty() || !lastBlock.mightHaveTerminator() ||
1265 !lastBlock.getTerminator()->hasTrait<OpTrait::IsTerminator>())
1266 return emitOpError() << "last block of cir.scope must be terminated";
1267 return success();
1268}
1269
1270LogicalResult cir::ScopeOp::fold(FoldAdaptor /*adaptor*/,
1271 SmallVectorImpl<OpFoldResult> &results) {
1272 // Only fold "trivial" scopes: a single block containing only a `cir.yield`.
1273 if (!getRegion().hasOneBlock())
1274 return failure();
1275 Block &block = getRegion().front();
1276 if (block.getOperations().size() != 1)
1277 return failure();
1278
1279 auto yield = dyn_cast<cir::YieldOp>(block.front());
1280 if (!yield)
1281 return failure();
1282
1283 // Only fold when the scope produces a value.
1284 if (getNumResults() != 1 || yield.getNumOperands() != 1)
1285 return failure();
1286
1287 results.push_back(yield.getOperand(0));
1288 return success();
1289}
1290
1291//===----------------------------------------------------------------------===//
1292// BrOp
1293//===----------------------------------------------------------------------===//
1294
1295mlir::SuccessorOperands cir::BrOp::getSuccessorOperands(unsigned index) {
1296 assert(index == 0 && "invalid successor index");
1297 return mlir::SuccessorOperands(getDestOperandsMutable());
1298}
1299
1300Block *cir::BrOp::getSuccessorForOperands(ArrayRef<Attribute>) {
1301 return getDest();
1302}
1303
1304//===----------------------------------------------------------------------===//
1305// IndirectBrCondOp
1306//===----------------------------------------------------------------------===//
1307
1308mlir::SuccessorOperands
1309cir::IndirectBrOp::getSuccessorOperands(unsigned index) {
1310 assert(index < getNumSuccessors() && "invalid successor index");
1311 return mlir::SuccessorOperands(getSuccOperandsMutable()[index]);
1312}
1313
1315 OpAsmParser &parser, Type &flagType,
1316 SmallVectorImpl<Block *> &succOperandBlocks,
1317 SmallVectorImpl<SmallVector<OpAsmParser::UnresolvedOperand>> &succOperands,
1318 SmallVectorImpl<SmallVector<Type>> &succOperandsTypes) {
1319 if (failed(parser.parseCommaSeparatedList(
1320 OpAsmParser::Delimiter::Square,
1321 [&]() {
1322 Block *destination = nullptr;
1323 SmallVector<OpAsmParser::UnresolvedOperand> operands;
1324 SmallVector<Type> operandTypes;
1325
1326 if (parser.parseSuccessor(destination).failed())
1327 return failure();
1328
1329 if (succeeded(parser.parseOptionalLParen())) {
1330 if (failed(parser.parseOperandList(
1331 operands, OpAsmParser::Delimiter::None)) ||
1332 failed(parser.parseColonTypeList(operandTypes)) ||
1333 failed(parser.parseRParen()))
1334 return failure();
1335 }
1336 succOperandBlocks.push_back(destination);
1337 succOperands.emplace_back(operands);
1338 succOperandsTypes.emplace_back(operandTypes);
1339 return success();
1340 },
1341 "successor blocks")))
1342 return failure();
1343 return success();
1344}
1345
1346void printIndirectBrOpSucessors(OpAsmPrinter &p, cir::IndirectBrOp op,
1347 Type flagType, SuccessorRange succs,
1348 OperandRangeRange succOperands,
1349 const TypeRangeRange &succOperandsTypes) {
1350 p << "[";
1351 llvm::interleave(
1352 llvm::zip(succs, succOperands),
1353 [&](auto i) {
1354 p.printNewline();
1355 p.printSuccessorAndUseList(std::get<0>(i), std::get<1>(i));
1356 },
1357 [&] { p << ','; });
1358 if (!succOperands.empty())
1359 p.printNewline();
1360 p << "]";
1361}
1362
1363//===----------------------------------------------------------------------===//
1364// BrCondOp
1365//===----------------------------------------------------------------------===//
1366
1367mlir::SuccessorOperands cir::BrCondOp::getSuccessorOperands(unsigned index) {
1368 assert(index < getNumSuccessors() && "invalid successor index");
1369 return SuccessorOperands(index == 0 ? getDestOperandsTrueMutable()
1370 : getDestOperandsFalseMutable());
1371}
1372
1373Block *cir::BrCondOp::getSuccessorForOperands(ArrayRef<Attribute> operands) {
1374 if (IntegerAttr condAttr = dyn_cast_if_present<IntegerAttr>(operands.front()))
1375 return condAttr.getValue().isOne() ? getDestTrue() : getDestFalse();
1376 return nullptr;
1377}
1378
1379//===----------------------------------------------------------------------===//
1380// CaseOp
1381//===----------------------------------------------------------------------===//
1382
1383void cir::CaseOp::getSuccessorRegions(
1384 mlir::RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &regions) {
1385 if (!point.isParent()) {
1386 regions.push_back(
1387 RegionSuccessor(getOperation(), getOperation()->getResults()));
1388 return;
1389 }
1390 regions.push_back(RegionSuccessor(&getCaseRegion()));
1391}
1392
1393void cir::CaseOp::build(OpBuilder &builder, OperationState &result,
1394 ArrayAttr value, CaseOpKind kind,
1395 OpBuilder::InsertPoint &insertPoint) {
1396 OpBuilder::InsertionGuard guardSwitch(builder);
1397 result.addAttribute("value", value);
1398 result.getOrAddProperties<Properties>().kind =
1399 cir::CaseOpKindAttr::get(builder.getContext(), kind);
1400 Region *caseRegion = result.addRegion();
1401 builder.createBlock(caseRegion);
1402
1403 insertPoint = builder.saveInsertionPoint();
1404}
1405
1406//===----------------------------------------------------------------------===//
1407// SwitchOp
1408//===----------------------------------------------------------------------===//
1409
1410void cir::SwitchOp::getSuccessorRegions(
1411 mlir::RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &region) {
1412 if (!point.isParent()) {
1413 region.push_back(
1414 RegionSuccessor(getOperation(), getOperation()->getResults()));
1415 return;
1416 }
1417
1418 region.push_back(RegionSuccessor(&getBody()));
1419}
1420
1421void cir::SwitchOp::build(OpBuilder &builder, OperationState &result,
1422 Value cond, BuilderOpStateCallbackRef switchBuilder) {
1423 assert(switchBuilder && "the builder callback for regions must be present");
1424 OpBuilder::InsertionGuard guardSwitch(builder);
1425 Region *switchRegion = result.addRegion();
1426 builder.createBlock(switchRegion);
1427 result.addOperands({cond});
1428 switchBuilder(builder, result.location, result);
1429}
1430
1431void cir::SwitchOp::collectCases(llvm::SmallVectorImpl<CaseOp> &cases) {
1432 walk<mlir::WalkOrder::PreOrder>([&](mlir::Operation *op) {
1433 // Don't walk in nested switch op.
1434 if (isa<cir::SwitchOp>(op) && op != *this)
1435 return WalkResult::skip();
1436
1437 if (auto caseOp = dyn_cast<cir::CaseOp>(op))
1438 cases.push_back(caseOp);
1439
1440 return WalkResult::advance();
1441 });
1442}
1443
1444bool cir::SwitchOp::isSimpleForm(llvm::SmallVectorImpl<CaseOp> &cases) {
1445 collectCases(cases);
1446
1447 if (getBody().empty())
1448 return false;
1449
1450 if (!isa<YieldOp>(getBody().front().back()))
1451 return false;
1452
1453 if (!llvm::all_of(getBody().front(),
1454 [](Operation &op) { return isa<CaseOp, YieldOp>(op); }))
1455 return false;
1456
1457 return llvm::all_of(cases, [this](CaseOp op) {
1458 return op->getParentOfType<SwitchOp>() == *this;
1459 });
1460}
1461
1462//===----------------------------------------------------------------------===//
1463// SwitchFlatOp
1464//===----------------------------------------------------------------------===//
1465
1466void cir::SwitchFlatOp::build(OpBuilder &builder, OperationState &result,
1467 Value value, Block *defaultDestination,
1468 ValueRange defaultOperands,
1469 ArrayRef<APInt> caseValues,
1470 BlockRange caseDestinations,
1471 ArrayRef<ValueRange> caseOperands) {
1472
1473 std::vector<mlir::Attribute> caseValuesAttrs;
1474 for (const APInt &val : caseValues)
1475 caseValuesAttrs.push_back(cir::IntAttr::get(value.getType(), val));
1476 mlir::ArrayAttr attrs = ArrayAttr::get(builder.getContext(), caseValuesAttrs);
1477
1478 build(builder, result, value, defaultOperands, caseOperands, attrs,
1479 defaultDestination, caseDestinations);
1480}
1481
1482/// <cases> ::= `[` (case (`,` case )* )? `]`
1483/// <case> ::= integer `:` bb-id (`(` ssa-use-and-type-list `)`)?
1484static ParseResult parseSwitchFlatOpCases(
1485 OpAsmParser &parser, Type flagType, mlir::ArrayAttr &caseValues,
1486 SmallVectorImpl<Block *> &caseDestinations,
1488 &caseOperands,
1489 SmallVectorImpl<llvm::SmallVector<Type>> &caseOperandTypes) {
1490 if (failed(parser.parseLSquare()))
1491 return failure();
1492 if (succeeded(parser.parseOptionalRSquare()))
1493 return success();
1495
1496 auto parseCase = [&]() {
1497 int64_t value = 0;
1498 if (failed(parser.parseInteger(value)))
1499 return failure();
1500
1501 values.push_back(cir::IntAttr::get(flagType, value));
1502
1503 Block *destination;
1505 llvm::SmallVector<Type> operandTypes;
1506 if (parser.parseColon() || parser.parseSuccessor(destination))
1507 return failure();
1508 if (!parser.parseOptionalLParen()) {
1509 if (parser.parseOperandList(operands, OpAsmParser::Delimiter::None,
1510 /*allowResultNumber=*/false) ||
1511 parser.parseColonTypeList(operandTypes) || parser.parseRParen())
1512 return failure();
1513 }
1514 caseDestinations.push_back(destination);
1515 caseOperands.emplace_back(operands);
1516 caseOperandTypes.emplace_back(operandTypes);
1517 return success();
1518 };
1519 if (failed(parser.parseCommaSeparatedList(parseCase)))
1520 return failure();
1521
1522 caseValues = ArrayAttr::get(flagType.getContext(), values);
1523
1524 return parser.parseRSquare();
1525}
1526
1527static void printSwitchFlatOpCases(OpAsmPrinter &p, cir::SwitchFlatOp op,
1528 Type flagType, mlir::ArrayAttr caseValues,
1529 SuccessorRange caseDestinations,
1530 OperandRangeRange caseOperands,
1531 const TypeRangeRange &caseOperandTypes) {
1532 p << '[';
1533 p.printNewline();
1534 if (!caseValues) {
1535 p << ']';
1536 return;
1537 }
1538
1539 size_t index = 0;
1540 llvm::interleave(
1541 llvm::zip(caseValues, caseDestinations),
1542 [&](auto i) {
1543 p << " ";
1544 mlir::Attribute a = std::get<0>(i);
1545 p << mlir::cast<cir::IntAttr>(a).getValue();
1546 p << ": ";
1547 p.printSuccessorAndUseList(std::get<1>(i), caseOperands[index++]);
1548 },
1549 [&] {
1550 p << ',';
1551 p.printNewline();
1552 });
1553 p.printNewline();
1554 p << ']';
1555}
1556
1557//===----------------------------------------------------------------------===//
1558// GlobalOp
1559//===----------------------------------------------------------------------===//
1560
1561static ParseResult parseConstantValue(OpAsmParser &parser,
1562 mlir::Attribute &valueAttr) {
1563 NamedAttrList attr;
1564 return parser.parseAttribute(valueAttr, "value", attr);
1565}
1566
1567static void printConstant(OpAsmPrinter &p, Attribute value) {
1568 p.printAttribute(value);
1569}
1570
1571mlir::LogicalResult cir::GlobalOp::verify() {
1572 // Verify that the initial value, if present, is either a unit attribute or
1573 // an attribute CIR supports.
1574 if (getInitialValue().has_value()) {
1575 if (checkConstantTypes(getOperation(), getSymType(), *getInitialValue())
1576 .failed())
1577 return failure();
1578 }
1579
1580 // TODO(CIR): Many other checks for properties that haven't been upstreamed
1581 // yet.
1582
1583 return success();
1584}
1585
1586void cir::GlobalOp::build(
1587 OpBuilder &odsBuilder, OperationState &odsState, llvm::StringRef sym_name,
1588 mlir::Type sym_type, bool isConstant, cir::GlobalLinkageKind linkage,
1589 function_ref<void(OpBuilder &, Location)> ctorBuilder,
1590 function_ref<void(OpBuilder &, Location)> dtorBuilder) {
1591 odsState.addAttribute(getSymNameAttrName(odsState.name),
1592 odsBuilder.getStringAttr(sym_name));
1593 odsState.addAttribute(getSymTypeAttrName(odsState.name),
1594 mlir::TypeAttr::get(sym_type));
1595 if (isConstant)
1596 odsState.addAttribute(getConstantAttrName(odsState.name),
1597 odsBuilder.getUnitAttr());
1598
1599 cir::GlobalLinkageKindAttr linkageAttr =
1600 cir::GlobalLinkageKindAttr::get(odsBuilder.getContext(), linkage);
1601 odsState.addAttribute(getLinkageAttrName(odsState.name), linkageAttr);
1602
1603 Region *ctorRegion = odsState.addRegion();
1604 if (ctorBuilder) {
1605 odsBuilder.createBlock(ctorRegion);
1606 ctorBuilder(odsBuilder, odsState.location);
1607 }
1608
1609 Region *dtorRegion = odsState.addRegion();
1610 if (dtorBuilder) {
1611 odsBuilder.createBlock(dtorRegion);
1612 dtorBuilder(odsBuilder, odsState.location);
1613 }
1614
1615 odsState.addAttribute(getGlobalVisibilityAttrName(odsState.name),
1616 cir::VisibilityAttr::get(odsBuilder.getContext()));
1617}
1618
1619/// Given the region at `index`, or the parent operation if `index` is None,
1620/// return the successor regions. These are the regions that may be selected
1621/// during the flow of control. `operands` is a set of optional attributes that
1622/// correspond to a constant value for each operand, or null if that operand is
1623/// not a constant.
1624void cir::GlobalOp::getSuccessorRegions(
1625 mlir::RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &regions) {
1626 // The `ctor` and `dtor` regions always branch back to the parent operation.
1627 if (!point.isParent()) {
1628 regions.push_back(
1629 RegionSuccessor(getOperation(), getOperation()->getResults()));
1630 return;
1631 }
1632
1633 // Don't consider the ctor region if it is empty.
1634 Region *ctorRegion = &this->getCtorRegion();
1635 if (ctorRegion->empty())
1636 ctorRegion = nullptr;
1637
1638 // Don't consider the dtor region if it is empty.
1639 Region *dtorRegion = &this->getCtorRegion();
1640 if (dtorRegion->empty())
1641 dtorRegion = nullptr;
1642
1643 // If the condition isn't constant, both regions may be executed.
1644 if (ctorRegion)
1645 regions.push_back(RegionSuccessor(ctorRegion));
1646 if (dtorRegion)
1647 regions.push_back(RegionSuccessor(dtorRegion));
1648}
1649
1650static void printGlobalOpTypeAndInitialValue(OpAsmPrinter &p, cir::GlobalOp op,
1651 TypeAttr type, Attribute initAttr,
1652 mlir::Region &ctorRegion,
1653 mlir::Region &dtorRegion) {
1654 auto printType = [&]() { p << ": " << type; };
1655 if (!op.isDeclaration()) {
1656 p << "= ";
1657 if (!ctorRegion.empty()) {
1658 p << "ctor ";
1659 printType();
1660 p << " ";
1661 p.printRegion(ctorRegion,
1662 /*printEntryBlockArgs=*/false,
1663 /*printBlockTerminators=*/false);
1664 } else {
1665 // This also prints the type...
1666 if (initAttr)
1667 printConstant(p, initAttr);
1668 }
1669
1670 if (!dtorRegion.empty()) {
1671 p << " dtor ";
1672 p.printRegion(dtorRegion,
1673 /*printEntryBlockArgs=*/false,
1674 /*printBlockTerminators=*/false);
1675 }
1676 } else {
1677 printType();
1678 }
1679}
1680
1681static ParseResult parseGlobalOpTypeAndInitialValue(OpAsmParser &parser,
1682 TypeAttr &typeAttr,
1683 Attribute &initialValueAttr,
1684 mlir::Region &ctorRegion,
1685 mlir::Region &dtorRegion) {
1686 mlir::Type opTy;
1687 if (parser.parseOptionalEqual().failed()) {
1688 // Absence of equal means a declaration, so we need to parse the type.
1689 // cir.global @a : !cir.int<s, 32>
1690 if (parser.parseColonType(opTy))
1691 return failure();
1692 } else {
1693 // Parse contructor, example:
1694 // cir.global @rgb = ctor : type { ... }
1695 if (!parser.parseOptionalKeyword("ctor")) {
1696 if (parser.parseColonType(opTy))
1697 return failure();
1698 auto parseLoc = parser.getCurrentLocation();
1699 if (parser.parseRegion(ctorRegion, /*arguments=*/{}, /*argTypes=*/{}))
1700 return failure();
1701 if (ensureRegionTerm(parser, ctorRegion, parseLoc).failed())
1702 return failure();
1703 } else {
1704 // Parse constant with initializer, examples:
1705 // cir.global @y = 3.400000e+00 : f32
1706 // cir.global @rgb = #cir.const_array<[...] : !cir.array<i8 x 3>>
1707 if (parseConstantValue(parser, initialValueAttr).failed())
1708 return failure();
1709
1710 assert(mlir::isa<mlir::TypedAttr>(initialValueAttr) &&
1711 "Non-typed attrs shouldn't appear here.");
1712 auto typedAttr = mlir::cast<mlir::TypedAttr>(initialValueAttr);
1713 opTy = typedAttr.getType();
1714 }
1715
1716 // Parse destructor, example:
1717 // dtor { ... }
1718 if (!parser.parseOptionalKeyword("dtor")) {
1719 auto parseLoc = parser.getCurrentLocation();
1720 if (parser.parseRegion(dtorRegion, /*arguments=*/{}, /*argTypes=*/{}))
1721 return failure();
1722 if (ensureRegionTerm(parser, dtorRegion, parseLoc).failed())
1723 return failure();
1724 }
1725 }
1726
1727 typeAttr = TypeAttr::get(opTy);
1728 return success();
1729}
1730
1731//===----------------------------------------------------------------------===//
1732// GetGlobalOp
1733//===----------------------------------------------------------------------===//
1734
1735LogicalResult
1736cir::GetGlobalOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1737 // Verify that the result type underlying pointer type matches the type of
1738 // the referenced cir.global or cir.func op.
1739 mlir::Operation *op =
1740 symbolTable.lookupNearestSymbolFrom(*this, getNameAttr());
1741 if (op == nullptr || !(isa<GlobalOp>(op) || isa<FuncOp>(op)))
1742 return emitOpError("'")
1743 << getName()
1744 << "' does not reference a valid cir.global or cir.func";
1745
1746 mlir::Type symTy;
1747 if (auto g = dyn_cast<GlobalOp>(op)) {
1748 symTy = g.getSymType();
1750 // Verify that for thread local global access, the global needs to
1751 // be marked with tls bits.
1752 if (getTls() && !g.getTlsModel())
1753 return emitOpError("access to global not marked thread local");
1754 } else if (auto f = dyn_cast<FuncOp>(op)) {
1755 symTy = f.getFunctionType();
1756 } else {
1757 llvm_unreachable("Unexpected operation for GetGlobalOp");
1758 }
1759
1760 auto resultType = dyn_cast<PointerType>(getAddr().getType());
1761 if (!resultType || symTy != resultType.getPointee())
1762 return emitOpError("result type pointee type '")
1763 << resultType.getPointee() << "' does not match type " << symTy
1764 << " of the global @" << getName();
1765
1766 return success();
1767}
1768
1769//===----------------------------------------------------------------------===//
1770// VTableAddrPointOp
1771//===----------------------------------------------------------------------===//
1772
1773LogicalResult
1774cir::VTableAddrPointOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1775 StringRef name = getName();
1776
1777 // Verify that the result type underlying pointer type matches the type of
1778 // the referenced cir.global.
1779 auto op =
1780 symbolTable.lookupNearestSymbolFrom<cir::GlobalOp>(*this, getNameAttr());
1781 if (!op)
1782 return emitOpError("'")
1783 << name << "' does not reference a valid cir.global";
1784 std::optional<mlir::Attribute> init = op.getInitialValue();
1785 if (!init)
1786 return success();
1787 if (!isa<cir::VTableAttr>(*init))
1788 return emitOpError("Expected #cir.vtable in initializer for global '")
1789 << name << "'";
1790 return success();
1791}
1792
1793//===----------------------------------------------------------------------===//
1794// VTTAddrPointOp
1795//===----------------------------------------------------------------------===//
1796
1797LogicalResult
1798cir::VTTAddrPointOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1799 // VTT ptr is not coming from a symbol.
1800 if (!getName())
1801 return success();
1802 StringRef name = *getName();
1803
1804 // Verify that the result type underlying pointer type matches the type of
1805 // the referenced cir.global op.
1806 auto op =
1807 symbolTable.lookupNearestSymbolFrom<cir::GlobalOp>(*this, getNameAttr());
1808 if (!op)
1809 return emitOpError("'")
1810 << name << "' does not reference a valid cir.global";
1811 std::optional<mlir::Attribute> init = op.getInitialValue();
1812 if (!init)
1813 return success();
1814 if (!isa<cir::ConstArrayAttr>(*init))
1815 return emitOpError(
1816 "Expected constant array in initializer for global VTT '")
1817 << name << "'";
1818 return success();
1819}
1820
1821LogicalResult cir::VTTAddrPointOp::verify() {
1822 // The operation uses either a symbol or a value to operate, but not both
1823 if (getName() && getSymAddr())
1824 return emitOpError("should use either a symbol or value, but not both");
1825
1826 // If not a symbol, stick with the concrete type used for getSymAddr.
1827 if (getSymAddr())
1828 return success();
1829
1830 mlir::Type resultType = getAddr().getType();
1831 mlir::Type resTy = cir::PointerType::get(
1832 cir::PointerType::get(cir::VoidType::get(getContext())));
1833
1834 if (resultType != resTy)
1835 return emitOpError("result type must be ")
1836 << resTy << ", but provided result type is " << resultType;
1837 return success();
1838}
1839
1840//===----------------------------------------------------------------------===//
1841// FuncOp
1842//===----------------------------------------------------------------------===//
1843
1844/// Returns the name used for the linkage attribute. This *must* correspond to
1845/// the name of the attribute in ODS.
1846static llvm::StringRef getLinkageAttrNameString() { return "linkage"; }
1847
1848void cir::FuncOp::build(OpBuilder &builder, OperationState &result,
1849 StringRef name, FuncType type,
1850 GlobalLinkageKind linkage) {
1851 result.addRegion();
1852 result.addAttribute(SymbolTable::getSymbolAttrName(),
1853 builder.getStringAttr(name));
1854 result.addAttribute(getFunctionTypeAttrName(result.name),
1855 TypeAttr::get(type));
1856 result.addAttribute(
1858 GlobalLinkageKindAttr::get(builder.getContext(), linkage));
1859 result.addAttribute(getGlobalVisibilityAttrName(result.name),
1860 cir::VisibilityAttr::get(builder.getContext()));
1861}
1862
1863ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) {
1864 llvm::SMLoc loc = parser.getCurrentLocation();
1865 mlir::Builder &builder = parser.getBuilder();
1866
1867 mlir::StringAttr builtinNameAttr = getBuiltinAttrName(state.name);
1868 mlir::StringAttr coroutineNameAttr = getCoroutineAttrName(state.name);
1869 mlir::StringAttr inlineKindNameAttr = getInlineKindAttrName(state.name);
1870 mlir::StringAttr lambdaNameAttr = getLambdaAttrName(state.name);
1871 mlir::StringAttr noProtoNameAttr = getNoProtoAttrName(state.name);
1872 mlir::StringAttr visNameAttr = getSymVisibilityAttrName(state.name);
1873 mlir::StringAttr visibilityNameAttr = getGlobalVisibilityAttrName(state.name);
1874 mlir::StringAttr dsoLocalNameAttr = getDsoLocalAttrName(state.name);
1875 mlir::StringAttr specialMemberAttr = getCxxSpecialMemberAttrName(state.name);
1876
1877 if (::mlir::succeeded(parser.parseOptionalKeyword(builtinNameAttr.strref())))
1878 state.addAttribute(builtinNameAttr, parser.getBuilder().getUnitAttr());
1879 if (::mlir::succeeded(
1880 parser.parseOptionalKeyword(coroutineNameAttr.strref())))
1881 state.addAttribute(coroutineNameAttr, parser.getBuilder().getUnitAttr());
1882
1883 // Parse optional inline kind attribute
1884 cir::InlineKindAttr inlineKindAttr;
1885 if (failed(parseInlineKindAttr(parser, inlineKindAttr)))
1886 return failure();
1887 if (inlineKindAttr)
1888 state.addAttribute(inlineKindNameAttr, inlineKindAttr);
1889
1890 if (::mlir::succeeded(parser.parseOptionalKeyword(lambdaNameAttr.strref())))
1891 state.addAttribute(lambdaNameAttr, parser.getBuilder().getUnitAttr());
1892 if (parser.parseOptionalKeyword(noProtoNameAttr).succeeded())
1893 state.addAttribute(noProtoNameAttr, parser.getBuilder().getUnitAttr());
1894
1895 // Default to external linkage if no keyword is provided.
1896 state.addAttribute(getLinkageAttrNameString(),
1897 GlobalLinkageKindAttr::get(
1898 parser.getContext(),
1900 parser, GlobalLinkageKind::ExternalLinkage)));
1901
1902 ::llvm::StringRef visAttrStr;
1903 if (parser.parseOptionalKeyword(&visAttrStr, {"private", "public", "nested"})
1904 .succeeded()) {
1905 state.addAttribute(visNameAttr,
1906 parser.getBuilder().getStringAttr(visAttrStr));
1907 }
1908
1909 cir::VisibilityAttr cirVisibilityAttr;
1910 parseVisibilityAttr(parser, cirVisibilityAttr);
1911 state.addAttribute(visibilityNameAttr, cirVisibilityAttr);
1912
1913 if (parser.parseOptionalKeyword(dsoLocalNameAttr).succeeded())
1914 state.addAttribute(dsoLocalNameAttr, parser.getBuilder().getUnitAttr());
1915
1916 StringAttr nameAttr;
1917 if (parser.parseSymbolName(nameAttr, SymbolTable::getSymbolAttrName(),
1918 state.attributes))
1919 return failure();
1923 bool isVariadic = false;
1924 if (function_interface_impl::parseFunctionSignatureWithArguments(
1925 parser, /*allowVariadic=*/true, arguments, isVariadic, resultTypes,
1926 resultAttrs))
1927 return failure();
1929 for (OpAsmParser::Argument &arg : arguments)
1930 argTypes.push_back(arg.type);
1931
1932 if (resultTypes.size() > 1) {
1933 return parser.emitError(
1934 loc, "functions with multiple return types are not supported");
1935 }
1936
1937 mlir::Type returnType =
1938 (resultTypes.empty() ? cir::VoidType::get(builder.getContext())
1939 : resultTypes.front());
1940
1941 cir::FuncType fnType = cir::FuncType::get(argTypes, returnType, isVariadic);
1942 if (!fnType)
1943 return failure();
1944 state.addAttribute(getFunctionTypeAttrName(state.name),
1945 TypeAttr::get(fnType));
1946
1947 bool hasAlias = false;
1948 mlir::StringAttr aliaseeNameAttr = getAliaseeAttrName(state.name);
1949 if (parser.parseOptionalKeyword("alias").succeeded()) {
1950 if (parser.parseLParen().failed())
1951 return failure();
1952 mlir::StringAttr aliaseeAttr;
1953 if (parser.parseOptionalSymbolName(aliaseeAttr).failed())
1954 return failure();
1955 state.addAttribute(aliaseeNameAttr, FlatSymbolRefAttr::get(aliaseeAttr));
1956 if (parser.parseRParen().failed())
1957 return failure();
1958 hasAlias = true;
1959 }
1960
1961 mlir::StringAttr personalityNameAttr = getPersonalityAttrName(state.name);
1962 if (parser.parseOptionalKeyword("personality").succeeded()) {
1963 if (parser.parseLParen().failed())
1964 return failure();
1965 mlir::StringAttr personalityAttr;
1966 if (parser.parseOptionalSymbolName(personalityAttr).failed())
1967 return failure();
1968 state.addAttribute(personalityNameAttr,
1969 FlatSymbolRefAttr::get(personalityAttr));
1970 if (parser.parseRParen().failed())
1971 return failure();
1972 }
1973
1974 auto parseGlobalDtorCtor =
1975 [&](StringRef keyword,
1976 llvm::function_ref<void(std::optional<int> prio)> createAttr)
1977 -> mlir::LogicalResult {
1978 if (mlir::succeeded(parser.parseOptionalKeyword(keyword))) {
1979 std::optional<int> priority;
1980 if (mlir::succeeded(parser.parseOptionalLParen())) {
1981 auto parsedPriority = mlir::FieldParser<int>::parse(parser);
1982 if (mlir::failed(parsedPriority))
1983 return parser.emitError(parser.getCurrentLocation(),
1984 "failed to parse 'priority', of type 'int'");
1985 priority = parsedPriority.value_or(int());
1986 // Parse literal ')'
1987 if (parser.parseRParen())
1988 return failure();
1989 }
1990 createAttr(priority);
1991 }
1992 return success();
1993 };
1994
1995 // Parse CXXSpecialMember attribute
1996 if (parser.parseOptionalKeyword("special_member").succeeded()) {
1997 cir::CXXCtorAttr ctorAttr;
1998 cir::CXXDtorAttr dtorAttr;
1999 cir::CXXAssignAttr assignAttr;
2000 if (parser.parseLess().failed())
2001 return failure();
2002 if (parser.parseOptionalAttribute(ctorAttr).has_value())
2003 state.addAttribute(specialMemberAttr, ctorAttr);
2004 else if (parser.parseOptionalAttribute(dtorAttr).has_value())
2005 state.addAttribute(specialMemberAttr, dtorAttr);
2006 else if (parser.parseOptionalAttribute(assignAttr).has_value())
2007 state.addAttribute(specialMemberAttr, assignAttr);
2008 if (parser.parseGreater().failed())
2009 return failure();
2010 }
2011
2012 if (parseGlobalDtorCtor("global_ctor", [&](std::optional<int> priority) {
2013 mlir::IntegerAttr globalCtorPriorityAttr =
2014 builder.getI32IntegerAttr(priority.value_or(65535));
2015 state.addAttribute(getGlobalCtorPriorityAttrName(state.name),
2016 globalCtorPriorityAttr);
2017 }).failed())
2018 return failure();
2019
2020 if (parseGlobalDtorCtor("global_dtor", [&](std::optional<int> priority) {
2021 mlir::IntegerAttr globalDtorPriorityAttr =
2022 builder.getI32IntegerAttr(priority.value_or(65535));
2023 state.addAttribute(getGlobalDtorPriorityAttrName(state.name),
2024 globalDtorPriorityAttr);
2025 }).failed())
2026 return failure();
2027
2028 // Parse the rest of the attributes.
2029 NamedAttrList parsedAttrs;
2030 if (parser.parseOptionalAttrDictWithKeyword(parsedAttrs))
2031 return failure();
2032
2033 for (StringRef disallowed : cir::FuncOp::getAttributeNames()) {
2034 if (parsedAttrs.get(disallowed))
2035 return parser.emitError(loc, "attribute '")
2036 << disallowed
2037 << "' should not be specified in the explicit attribute list";
2038 }
2039
2040 state.attributes.append(parsedAttrs);
2041
2042 // Parse the optional function body.
2043 auto *body = state.addRegion();
2044 OptionalParseResult parseResult = parser.parseOptionalRegion(
2045 *body, arguments, /*enableNameShadowing=*/false);
2046 if (parseResult.has_value()) {
2047 if (hasAlias)
2048 return parser.emitError(loc, "function alias shall not have a body");
2049 if (failed(*parseResult))
2050 return failure();
2051 // Function body was parsed, make sure its not empty.
2052 if (body->empty())
2053 return parser.emitError(loc, "expected non-empty function body");
2054 }
2055
2056 return success();
2057}
2058
2059// This function corresponds to `llvm::GlobalValue::isDeclaration` and should
2060// have a similar implementation. We don't currently ifuncs or materializable
2061// functions, but those should be handled here as they are implemented.
2062bool cir::FuncOp::isDeclaration() {
2064
2065 std::optional<StringRef> aliasee = getAliasee();
2066 if (!aliasee)
2067 return getFunctionBody().empty();
2068
2069 // Aliases are always definitions.
2070 return false;
2071}
2072
2073bool cir::FuncOp::isCXXSpecialMemberFunction() {
2074 return getCxxSpecialMemberAttr() != nullptr;
2075}
2076
2077bool cir::FuncOp::isCxxConstructor() {
2078 auto attr = getCxxSpecialMemberAttr();
2079 return attr && dyn_cast<CXXCtorAttr>(attr);
2080}
2081
2082bool cir::FuncOp::isCxxDestructor() {
2083 auto attr = getCxxSpecialMemberAttr();
2084 return attr && dyn_cast<CXXDtorAttr>(attr);
2085}
2086
2087bool cir::FuncOp::isCxxSpecialAssignment() {
2088 auto attr = getCxxSpecialMemberAttr();
2089 return attr && dyn_cast<CXXAssignAttr>(attr);
2090}
2091
2092std::optional<CtorKind> cir::FuncOp::getCxxConstructorKind() {
2093 mlir::Attribute attr = getCxxSpecialMemberAttr();
2094 if (attr) {
2095 if (auto ctor = dyn_cast<CXXCtorAttr>(attr))
2096 return ctor.getCtorKind();
2097 }
2098 return std::nullopt;
2099}
2100
2101std::optional<AssignKind> cir::FuncOp::getCxxSpecialAssignKind() {
2102 mlir::Attribute attr = getCxxSpecialMemberAttr();
2103 if (attr) {
2104 if (auto assign = dyn_cast<CXXAssignAttr>(attr))
2105 return assign.getAssignKind();
2106 }
2107 return std::nullopt;
2108}
2109
2110bool cir::FuncOp::isCxxTrivialMemberFunction() {
2111 mlir::Attribute attr = getCxxSpecialMemberAttr();
2112 if (attr) {
2113 if (auto ctor = dyn_cast<CXXCtorAttr>(attr))
2114 return ctor.getIsTrivial();
2115 if (auto dtor = dyn_cast<CXXDtorAttr>(attr))
2116 return dtor.getIsTrivial();
2117 if (auto assign = dyn_cast<CXXAssignAttr>(attr))
2118 return assign.getIsTrivial();
2119 }
2120 return false;
2121}
2122
2123mlir::Region *cir::FuncOp::getCallableRegion() {
2124 // TODO(CIR): This function will have special handling for aliases and a
2125 // check for an external function, once those features have been upstreamed.
2126 return &getBody();
2127}
2128
2129void cir::FuncOp::print(OpAsmPrinter &p) {
2130 if (getBuiltin())
2131 p << " builtin";
2132
2133 if (getCoroutine())
2134 p << " coroutine";
2135
2136 printInlineKindAttr(p, getInlineKindAttr());
2137
2138 if (getLambda())
2139 p << " lambda";
2140
2141 if (getNoProto())
2142 p << " no_proto";
2143
2144 if (getComdat())
2145 p << " comdat";
2146
2147 if (getLinkage() != GlobalLinkageKind::ExternalLinkage)
2148 p << ' ' << stringifyGlobalLinkageKind(getLinkage());
2149
2150 mlir::SymbolTable::Visibility vis = getVisibility();
2151 if (vis != mlir::SymbolTable::Visibility::Public)
2152 p << ' ' << vis;
2153
2154 cir::VisibilityAttr cirVisibilityAttr = getGlobalVisibilityAttr();
2155 if (!cirVisibilityAttr.isDefault()) {
2156 p << ' ';
2157 printVisibilityAttr(p, cirVisibilityAttr);
2158 }
2159
2160 if (getDsoLocal())
2161 p << " dso_local";
2162
2163 p << ' ';
2164 p.printSymbolName(getSymName());
2165 cir::FuncType fnType = getFunctionType();
2166 function_interface_impl::printFunctionSignature(
2167 p, *this, fnType.getInputs(), fnType.isVarArg(), fnType.getReturnTypes());
2168
2169 if (std::optional<StringRef> aliaseeName = getAliasee()) {
2170 p << " alias(";
2171 p.printSymbolName(*aliaseeName);
2172 p << ")";
2173 }
2174
2175 if (std::optional<StringRef> personalityName = getPersonality()) {
2176 p << " personality(";
2177 p.printSymbolName(*personalityName);
2178 p << ")";
2179 }
2180
2181 if (auto specialMemberAttr = getCxxSpecialMember()) {
2182 p << " special_member<";
2183 p.printAttribute(*specialMemberAttr);
2184 p << '>';
2185 }
2186
2187 if (auto globalCtorPriority = getGlobalCtorPriority()) {
2188 p << " global_ctor";
2189 if (globalCtorPriority.value() != 65535)
2190 p << "(" << globalCtorPriority.value() << ")";
2191 }
2192
2193 if (auto globalDtorPriority = getGlobalDtorPriority()) {
2194 p << " global_dtor";
2195 if (globalDtorPriority.value() != 65535)
2196 p << "(" << globalDtorPriority.value() << ")";
2197 }
2198
2199 function_interface_impl::printFunctionAttributes(
2200 p, *this, cir::FuncOp::getAttributeNames());
2201
2202 // Print the body if this is not an external function.
2203 Region &body = getOperation()->getRegion(0);
2204 if (!body.empty()) {
2205 p << ' ';
2206 p.printRegion(body, /*printEntryBlockArgs=*/false,
2207 /*printBlockTerminators=*/true);
2208 }
2209}
2210
2211mlir::LogicalResult cir::FuncOp::verify() {
2212
2213 if (!isDeclaration() && getCoroutine()) {
2214 bool foundAwait = false;
2215 this->walk([&](Operation *op) {
2216 if (auto await = dyn_cast<AwaitOp>(op)) {
2217 foundAwait = true;
2218 return;
2219 }
2220 });
2221 if (!foundAwait)
2222 return emitOpError()
2223 << "coroutine body must use at least one cir.await op";
2224 }
2225
2226 llvm::SmallSet<llvm::StringRef, 16> labels;
2227 llvm::SmallSet<llvm::StringRef, 16> gotos;
2228 llvm::SmallSet<llvm::StringRef, 16> blockAddresses;
2229 bool invalidBlockAddress = false;
2230 getOperation()->walk([&](mlir::Operation *op) {
2231 if (auto lab = dyn_cast<cir::LabelOp>(op)) {
2232 labels.insert(lab.getLabel());
2233 } else if (auto goTo = dyn_cast<cir::GotoOp>(op)) {
2234 gotos.insert(goTo.getLabel());
2235 } else if (auto blkAdd = dyn_cast<cir::BlockAddressOp>(op)) {
2236 if (blkAdd.getBlockAddrInfoAttr().getFunc().getAttr() != getSymName()) {
2237 // Stop the walk early, no need to continue
2238 invalidBlockAddress = true;
2239 return mlir::WalkResult::interrupt();
2240 }
2241 blockAddresses.insert(blkAdd.getBlockAddrInfoAttr().getLabel());
2242 }
2243 return mlir::WalkResult::advance();
2244 });
2245
2246 if (invalidBlockAddress)
2247 return emitOpError() << "blockaddress references a different function";
2248
2249 llvm::SmallSet<llvm::StringRef, 16> mismatched;
2250 if (!labels.empty() || !gotos.empty()) {
2251 mismatched = llvm::set_difference(gotos, labels);
2252
2253 if (!mismatched.empty())
2254 return emitOpError() << "goto/label mismatch";
2255 }
2256
2257 mismatched.clear();
2258
2259 if (!labels.empty() || !blockAddresses.empty()) {
2260 mismatched = llvm::set_difference(blockAddresses, labels);
2261
2262 if (!mismatched.empty())
2263 return emitOpError()
2264 << "expects an existing label target in the referenced function";
2265 }
2266
2267 return success();
2268}
2269
2270//===----------------------------------------------------------------------===//
2271// BinOp
2272//===----------------------------------------------------------------------===//
2273LogicalResult cir::BinOp::verify() {
2274 bool noWrap = getNoUnsignedWrap() || getNoSignedWrap();
2275 bool saturated = getSaturated();
2276
2277 if (!isa<cir::IntType>(getType()) && noWrap)
2278 return emitError()
2279 << "only operations on integer values may have nsw/nuw flags";
2280
2281 bool noWrapOps = getKind() == cir::BinOpKind::Add ||
2282 getKind() == cir::BinOpKind::Sub ||
2283 getKind() == cir::BinOpKind::Mul;
2284
2285 bool saturatedOps =
2286 getKind() == cir::BinOpKind::Add || getKind() == cir::BinOpKind::Sub;
2287
2288 if (noWrap && !noWrapOps)
2289 return emitError() << "The nsw/nuw flags are applicable to opcodes: 'add', "
2290 "'sub' and 'mul'";
2291 if (saturated && !saturatedOps)
2292 return emitError() << "The saturated flag is applicable to opcodes: 'add' "
2293 "and 'sub'";
2294 if (noWrap && saturated)
2295 return emitError() << "The nsw/nuw flags and the saturated flag are "
2296 "mutually exclusive";
2297
2298 return mlir::success();
2299}
2300
2301//===----------------------------------------------------------------------===//
2302// TernaryOp
2303//===----------------------------------------------------------------------===//
2304
2305/// Given the region at `point`, or the parent operation if `point` is None,
2306/// return the successor regions. These are the regions that may be selected
2307/// during the flow of control. `operands` is a set of optional attributes that
2308/// correspond to a constant value for each operand, or null if that operand is
2309/// not a constant.
2310void cir::TernaryOp::getSuccessorRegions(
2311 mlir::RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &regions) {
2312 // The `true` and the `false` region branch back to the parent operation.
2313 if (!point.isParent()) {
2314 regions.push_back(RegionSuccessor(getOperation(), this->getODSResults(0)));
2315 return;
2316 }
2317
2318 // When branching from the parent operation, both the true and false
2319 // regions are considered possible successors
2320 regions.push_back(RegionSuccessor(&getTrueRegion()));
2321 regions.push_back(RegionSuccessor(&getFalseRegion()));
2322}
2323
2324void cir::TernaryOp::build(
2325 OpBuilder &builder, OperationState &result, Value cond,
2326 function_ref<void(OpBuilder &, Location)> trueBuilder,
2327 function_ref<void(OpBuilder &, Location)> falseBuilder) {
2328 result.addOperands(cond);
2329 OpBuilder::InsertionGuard guard(builder);
2330 Region *trueRegion = result.addRegion();
2331 builder.createBlock(trueRegion);
2332 trueBuilder(builder, result.location);
2333 Region *falseRegion = result.addRegion();
2334 builder.createBlock(falseRegion);
2335 falseBuilder(builder, result.location);
2336
2337 // Get result type from whichever branch has a yield (the other may have
2338 // unreachable from a throw expression)
2339 auto yield =
2340 dyn_cast_or_null<cir::YieldOp>(trueRegion->back().getTerminator());
2341 if (!yield)
2342 yield = dyn_cast_or_null<cir::YieldOp>(falseRegion->back().getTerminator());
2343
2344 assert((yield && yield.getNumOperands() <= 1) &&
2345 "expected zero or one result type");
2346 if (yield.getNumOperands() == 1)
2347 result.addTypes(TypeRange{yield.getOperandTypes().front()});
2348}
2349
2350//===----------------------------------------------------------------------===//
2351// SelectOp
2352//===----------------------------------------------------------------------===//
2353
2354OpFoldResult cir::SelectOp::fold(FoldAdaptor adaptor) {
2355 mlir::Attribute condition = adaptor.getCondition();
2356 if (condition) {
2357 bool conditionValue = mlir::cast<cir::BoolAttr>(condition).getValue();
2358 return conditionValue ? getTrueValue() : getFalseValue();
2359 }
2360
2361 // cir.select if %0 then x else x -> x
2362 mlir::Attribute trueValue = adaptor.getTrueValue();
2363 mlir::Attribute falseValue = adaptor.getFalseValue();
2364 if (trueValue == falseValue)
2365 return trueValue;
2366 if (getTrueValue() == getFalseValue())
2367 return getTrueValue();
2368
2369 return {};
2370}
2371
2372LogicalResult cir::SelectOp::verify() {
2373 // AllTypesMatch already guarantees trueVal and falseVal have matching types.
2374 auto condTy = dyn_cast<cir::VectorType>(getCondition().getType());
2375
2376 // If condition is not a vector, no further checks are needed.
2377 if (!condTy)
2378 return success();
2379
2380 // When condition is a vector, both other operands must also be vectors.
2381 if (!isa<cir::VectorType>(getTrueValue().getType()) ||
2382 !isa<cir::VectorType>(getFalseValue().getType())) {
2383 return emitOpError()
2384 << "expected both true and false operands to be vector types "
2385 "when the condition is a vector boolean type";
2386 }
2387
2388 return success();
2389}
2390
2391//===----------------------------------------------------------------------===//
2392// ShiftOp
2393//===----------------------------------------------------------------------===//
2394LogicalResult cir::ShiftOp::verify() {
2395 mlir::Operation *op = getOperation();
2396 auto op0VecTy = mlir::dyn_cast<cir::VectorType>(op->getOperand(0).getType());
2397 auto op1VecTy = mlir::dyn_cast<cir::VectorType>(op->getOperand(1).getType());
2398 if (!op0VecTy ^ !op1VecTy)
2399 return emitOpError() << "input types cannot be one vector and one scalar";
2400
2401 if (op0VecTy) {
2402 if (op0VecTy.getSize() != op1VecTy.getSize())
2403 return emitOpError() << "input vector types must have the same size";
2404
2405 auto opResultTy = mlir::dyn_cast<cir::VectorType>(getType());
2406 if (!opResultTy)
2407 return emitOpError() << "the type of the result must be a vector "
2408 << "if it is vector shift";
2409
2410 auto op0VecEleTy = mlir::cast<cir::IntType>(op0VecTy.getElementType());
2411 auto op1VecEleTy = mlir::cast<cir::IntType>(op1VecTy.getElementType());
2412 if (op0VecEleTy.getWidth() != op1VecEleTy.getWidth())
2413 return emitOpError()
2414 << "vector operands do not have the same elements sizes";
2415
2416 auto resVecEleTy = mlir::cast<cir::IntType>(opResultTy.getElementType());
2417 if (op0VecEleTy.getWidth() != resVecEleTy.getWidth())
2418 return emitOpError() << "vector operands and result type do not have the "
2419 "same elements sizes";
2420 }
2421
2422 return mlir::success();
2423}
2424
2425//===----------------------------------------------------------------------===//
2426// LabelOp Definitions
2427//===----------------------------------------------------------------------===//
2428
2429LogicalResult cir::LabelOp::verify() {
2430 mlir::Operation *op = getOperation();
2431 mlir::Block *blk = op->getBlock();
2432 if (&blk->front() != op)
2433 return emitError() << "must be the first operation in a block";
2434
2435 return mlir::success();
2436}
2437
2438//===----------------------------------------------------------------------===//
2439// UnaryOp
2440//===----------------------------------------------------------------------===//
2441
2442LogicalResult cir::UnaryOp::verify() {
2443 switch (getKind()) {
2444 case cir::UnaryOpKind::Inc:
2445 case cir::UnaryOpKind::Dec:
2446 case cir::UnaryOpKind::Plus:
2447 case cir::UnaryOpKind::Minus:
2448 case cir::UnaryOpKind::Not:
2449 // Nothing to verify.
2450 return success();
2451 }
2452
2453 llvm_unreachable("Unknown UnaryOp kind?");
2454}
2455
2456static bool isBoolNot(cir::UnaryOp op) {
2457 return isa<cir::BoolType>(op.getInput().getType()) &&
2458 op.getKind() == cir::UnaryOpKind::Not;
2459}
2460
2461// This folder simplifies the sequential boolean not operations.
2462// For instance, the next two unary operations will be eliminated:
2463//
2464// ```mlir
2465// %1 = cir.unary(not, %0) : !cir.bool, !cir.bool
2466// %2 = cir.unary(not, %1) : !cir.bool, !cir.bool
2467// ```
2468//
2469// and the argument of the first one (%0) will be used instead.
2470OpFoldResult cir::UnaryOp::fold(FoldAdaptor adaptor) {
2471 if (auto poison =
2472 mlir::dyn_cast_if_present<cir::PoisonAttr>(adaptor.getInput())) {
2473 // Propagate poison values
2474 return poison;
2475 }
2476
2477 if (isBoolNot(*this))
2478 if (auto previous = getInput().getDefiningOp<cir::UnaryOp>())
2479 if (isBoolNot(previous))
2480 return previous.getInput();
2481
2482 return {};
2483}
2484//===----------------------------------------------------------------------===//
2485// AwaitOp
2486//===----------------------------------------------------------------------===//
2487
2488void cir::AwaitOp::build(OpBuilder &builder, OperationState &result,
2489 cir::AwaitKind kind, BuilderCallbackRef readyBuilder,
2490 BuilderCallbackRef suspendBuilder,
2491 BuilderCallbackRef resumeBuilder) {
2492 result.addAttribute(getKindAttrName(result.name),
2493 cir::AwaitKindAttr::get(builder.getContext(), kind));
2494 {
2495 OpBuilder::InsertionGuard guard(builder);
2496 Region *readyRegion = result.addRegion();
2497 builder.createBlock(readyRegion);
2498 readyBuilder(builder, result.location);
2499 }
2500
2501 {
2502 OpBuilder::InsertionGuard guard(builder);
2503 Region *suspendRegion = result.addRegion();
2504 builder.createBlock(suspendRegion);
2505 suspendBuilder(builder, result.location);
2506 }
2507
2508 {
2509 OpBuilder::InsertionGuard guard(builder);
2510 Region *resumeRegion = result.addRegion();
2511 builder.createBlock(resumeRegion);
2512 resumeBuilder(builder, result.location);
2513 }
2514}
2515
2516void cir::AwaitOp::getSuccessorRegions(
2517 mlir::RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &regions) {
2518 // If any index all the underlying regions branch back to the parent
2519 // operation.
2520 if (!point.isParent()) {
2521 regions.push_back(
2522 RegionSuccessor(getOperation(), getOperation()->getResults()));
2523 return;
2524 }
2525
2526 // TODO: retrieve information from the promise and only push the
2527 // necessary ones. Example: `std::suspend_never` on initial or final
2528 // await's might allow suspend region to be skipped.
2529 regions.push_back(RegionSuccessor(&this->getReady()));
2530 regions.push_back(RegionSuccessor(&this->getSuspend()));
2531 regions.push_back(RegionSuccessor(&this->getResume()));
2532}
2533
2534LogicalResult cir::AwaitOp::verify() {
2535 if (!isa<ConditionOp>(this->getReady().back().getTerminator()))
2536 return emitOpError("ready region must end with cir.condition");
2537 return success();
2538}
2539
2540//===----------------------------------------------------------------------===//
2541// CopyOp Definitions
2542//===----------------------------------------------------------------------===//
2543
2544LogicalResult cir::CopyOp::verify() {
2545 // A data layout is required for us to know the number of bytes to be copied.
2546 if (!getType().getPointee().hasTrait<DataLayoutTypeInterface::Trait>())
2547 return emitError() << "missing data layout for pointee type";
2548
2549 if (getSrc() == getDst())
2550 return emitError() << "source and destination are the same";
2551
2552 return mlir::success();
2553}
2554
2555//===----------------------------------------------------------------------===//
2556// GetRuntimeMemberOp Definitions
2557//===----------------------------------------------------------------------===//
2558
2559LogicalResult cir::GetRuntimeMemberOp::verify() {
2560 auto recordTy = mlir::cast<RecordType>(getAddr().getType().getPointee());
2561 cir::DataMemberType memberPtrTy = getMember().getType();
2562
2563 if (recordTy != memberPtrTy.getClassTy())
2564 return emitError() << "record type does not match the member pointer type";
2565 if (getType().getPointee() != memberPtrTy.getMemberTy())
2566 return emitError() << "result type does not match the member pointer type";
2567 return mlir::success();
2568}
2569
2570//===----------------------------------------------------------------------===//
2571// GetMemberOp Definitions
2572//===----------------------------------------------------------------------===//
2573
2574LogicalResult cir::GetMemberOp::verify() {
2575 const auto recordTy = dyn_cast<RecordType>(getAddrTy().getPointee());
2576 if (!recordTy)
2577 return emitError() << "expected pointer to a record type";
2578
2579 if (recordTy.getMembers().size() <= getIndex())
2580 return emitError() << "member index out of bounds";
2581
2582 if (recordTy.getMembers()[getIndex()] != getType().getPointee())
2583 return emitError() << "member type mismatch";
2584
2585 return mlir::success();
2586}
2587
2588//===----------------------------------------------------------------------===//
2589// ExtractMemberOp Definitions
2590//===----------------------------------------------------------------------===//
2591
2592LogicalResult cir::ExtractMemberOp::verify() {
2593 auto recordTy = mlir::cast<cir::RecordType>(getRecord().getType());
2594 if (recordTy.getKind() == cir::RecordType::Union)
2595 return emitError()
2596 << "cir.extract_member currently does not support unions";
2597 if (recordTy.getMembers().size() <= getIndex())
2598 return emitError() << "member index out of bounds";
2599 if (recordTy.getMembers()[getIndex()] != getType())
2600 return emitError() << "member type mismatch";
2601 return mlir::success();
2602}
2603
2604//===----------------------------------------------------------------------===//
2605// VecCreateOp
2606//===----------------------------------------------------------------------===//
2607
2608OpFoldResult cir::VecCreateOp::fold(FoldAdaptor adaptor) {
2609 if (llvm::any_of(getElements(), [](mlir::Value value) {
2610 return !value.getDefiningOp<cir::ConstantOp>();
2611 }))
2612 return {};
2613
2614 return cir::ConstVectorAttr::get(
2615 getType(), mlir::ArrayAttr::get(getContext(), adaptor.getElements()));
2616}
2617
2618LogicalResult cir::VecCreateOp::verify() {
2619 // Verify that the number of arguments matches the number of elements in the
2620 // vector, and that the type of all the arguments matches the type of the
2621 // elements in the vector.
2622 const cir::VectorType vecTy = getType();
2623 if (getElements().size() != vecTy.getSize()) {
2624 return emitOpError() << "operand count of " << getElements().size()
2625 << " doesn't match vector type " << vecTy
2626 << " element count of " << vecTy.getSize();
2627 }
2628
2629 const mlir::Type elementType = vecTy.getElementType();
2630 for (const mlir::Value element : getElements()) {
2631 if (element.getType() != elementType) {
2632 return emitOpError() << "operand type " << element.getType()
2633 << " doesn't match vector element type "
2634 << elementType;
2635 }
2636 }
2637
2638 return success();
2639}
2640
2641//===----------------------------------------------------------------------===//
2642// VecExtractOp
2643//===----------------------------------------------------------------------===//
2644
2645OpFoldResult cir::VecExtractOp::fold(FoldAdaptor adaptor) {
2646 const auto vectorAttr =
2647 llvm::dyn_cast_if_present<cir::ConstVectorAttr>(adaptor.getVec());
2648 if (!vectorAttr)
2649 return {};
2650
2651 const auto indexAttr =
2652 llvm::dyn_cast_if_present<cir::IntAttr>(adaptor.getIndex());
2653 if (!indexAttr)
2654 return {};
2655
2656 const mlir::ArrayAttr elements = vectorAttr.getElts();
2657 const uint64_t index = indexAttr.getUInt();
2658 if (index >= elements.size())
2659 return {};
2660
2661 return elements[index];
2662}
2663
2664//===----------------------------------------------------------------------===//
2665// VecCmpOp
2666//===----------------------------------------------------------------------===//
2667
2668OpFoldResult cir::VecCmpOp::fold(FoldAdaptor adaptor) {
2669 auto lhsVecAttr =
2670 mlir::dyn_cast_if_present<cir::ConstVectorAttr>(adaptor.getLhs());
2671 auto rhsVecAttr =
2672 mlir::dyn_cast_if_present<cir::ConstVectorAttr>(adaptor.getRhs());
2673 if (!lhsVecAttr || !rhsVecAttr)
2674 return {};
2675
2676 mlir::Type inputElemTy =
2677 mlir::cast<cir::VectorType>(lhsVecAttr.getType()).getElementType();
2678 if (!isAnyIntegerOrFloatingPointType(inputElemTy))
2679 return {};
2680
2681 cir::CmpOpKind opKind = adaptor.getKind();
2682 mlir::ArrayAttr lhsVecElhs = lhsVecAttr.getElts();
2683 mlir::ArrayAttr rhsVecElhs = rhsVecAttr.getElts();
2684 uint64_t vecSize = lhsVecElhs.size();
2685
2686 SmallVector<mlir::Attribute, 16> elements(vecSize);
2687 bool isIntAttr = vecSize && mlir::isa<cir::IntAttr>(lhsVecElhs[0]);
2688 for (uint64_t i = 0; i < vecSize; i++) {
2689 mlir::Attribute lhsAttr = lhsVecElhs[i];
2690 mlir::Attribute rhsAttr = rhsVecElhs[i];
2691 int cmpResult = 0;
2692 switch (opKind) {
2693 case cir::CmpOpKind::lt: {
2694 if (isIntAttr) {
2695 cmpResult = mlir::cast<cir::IntAttr>(lhsAttr).getSInt() <
2696 mlir::cast<cir::IntAttr>(rhsAttr).getSInt();
2697 } else {
2698 cmpResult = mlir::cast<cir::FPAttr>(lhsAttr).getValue() <
2699 mlir::cast<cir::FPAttr>(rhsAttr).getValue();
2700 }
2701 break;
2702 }
2703 case cir::CmpOpKind::le: {
2704 if (isIntAttr) {
2705 cmpResult = mlir::cast<cir::IntAttr>(lhsAttr).getSInt() <=
2706 mlir::cast<cir::IntAttr>(rhsAttr).getSInt();
2707 } else {
2708 cmpResult = mlir::cast<cir::FPAttr>(lhsAttr).getValue() <=
2709 mlir::cast<cir::FPAttr>(rhsAttr).getValue();
2710 }
2711 break;
2712 }
2713 case cir::CmpOpKind::gt: {
2714 if (isIntAttr) {
2715 cmpResult = mlir::cast<cir::IntAttr>(lhsAttr).getSInt() >
2716 mlir::cast<cir::IntAttr>(rhsAttr).getSInt();
2717 } else {
2718 cmpResult = mlir::cast<cir::FPAttr>(lhsAttr).getValue() >
2719 mlir::cast<cir::FPAttr>(rhsAttr).getValue();
2720 }
2721 break;
2722 }
2723 case cir::CmpOpKind::ge: {
2724 if (isIntAttr) {
2725 cmpResult = mlir::cast<cir::IntAttr>(lhsAttr).getSInt() >=
2726 mlir::cast<cir::IntAttr>(rhsAttr).getSInt();
2727 } else {
2728 cmpResult = mlir::cast<cir::FPAttr>(lhsAttr).getValue() >=
2729 mlir::cast<cir::FPAttr>(rhsAttr).getValue();
2730 }
2731 break;
2732 }
2733 case cir::CmpOpKind::eq: {
2734 if (isIntAttr) {
2735 cmpResult = mlir::cast<cir::IntAttr>(lhsAttr).getSInt() ==
2736 mlir::cast<cir::IntAttr>(rhsAttr).getSInt();
2737 } else {
2738 cmpResult = mlir::cast<cir::FPAttr>(lhsAttr).getValue() ==
2739 mlir::cast<cir::FPAttr>(rhsAttr).getValue();
2740 }
2741 break;
2742 }
2743 case cir::CmpOpKind::ne: {
2744 if (isIntAttr) {
2745 cmpResult = mlir::cast<cir::IntAttr>(lhsAttr).getSInt() !=
2746 mlir::cast<cir::IntAttr>(rhsAttr).getSInt();
2747 } else {
2748 cmpResult = mlir::cast<cir::FPAttr>(lhsAttr).getValue() !=
2749 mlir::cast<cir::FPAttr>(rhsAttr).getValue();
2750 }
2751 break;
2752 }
2753 }
2754
2755 elements[i] = cir::IntAttr::get(getType().getElementType(), cmpResult);
2756 }
2757
2758 return cir::ConstVectorAttr::get(
2759 getType(), mlir::ArrayAttr::get(getContext(), elements));
2760}
2761
2762//===----------------------------------------------------------------------===//
2763// VecShuffleOp
2764//===----------------------------------------------------------------------===//
2765
2766OpFoldResult cir::VecShuffleOp::fold(FoldAdaptor adaptor) {
2767 auto vec1Attr =
2768 mlir::dyn_cast_if_present<cir::ConstVectorAttr>(adaptor.getVec1());
2769 auto vec2Attr =
2770 mlir::dyn_cast_if_present<cir::ConstVectorAttr>(adaptor.getVec2());
2771 if (!vec1Attr || !vec2Attr)
2772 return {};
2773
2774 mlir::Type vec1ElemTy =
2775 mlir::cast<cir::VectorType>(vec1Attr.getType()).getElementType();
2776
2777 mlir::ArrayAttr vec1Elts = vec1Attr.getElts();
2778 mlir::ArrayAttr vec2Elts = vec2Attr.getElts();
2779 mlir::ArrayAttr indicesElts = adaptor.getIndices();
2780
2782 elements.reserve(indicesElts.size());
2783
2784 uint64_t vec1Size = vec1Elts.size();
2785 for (const auto &idxAttr : indicesElts.getAsRange<cir::IntAttr>()) {
2786 if (idxAttr.getSInt() == -1) {
2787 elements.push_back(cir::UndefAttr::get(vec1ElemTy));
2788 continue;
2789 }
2790
2791 uint64_t idxValue = idxAttr.getUInt();
2792 elements.push_back(idxValue < vec1Size ? vec1Elts[idxValue]
2793 : vec2Elts[idxValue - vec1Size]);
2794 }
2795
2796 return cir::ConstVectorAttr::get(
2797 getType(), mlir::ArrayAttr::get(getContext(), elements));
2798}
2799
2800LogicalResult cir::VecShuffleOp::verify() {
2801 // The number of elements in the indices array must match the number of
2802 // elements in the result type.
2803 if (getIndices().size() != getResult().getType().getSize()) {
2804 return emitOpError() << ": the number of elements in " << getIndices()
2805 << " and " << getResult().getType() << " don't match";
2806 }
2807
2808 // The element types of the two input vectors and of the result type must
2809 // match.
2810 if (getVec1().getType().getElementType() !=
2811 getResult().getType().getElementType()) {
2812 return emitOpError() << ": element types of " << getVec1().getType()
2813 << " and " << getResult().getType() << " don't match";
2814 }
2815
2816 const uint64_t maxValidIndex =
2817 getVec1().getType().getSize() + getVec2().getType().getSize() - 1;
2818 if (llvm::any_of(
2819 getIndices().getAsRange<cir::IntAttr>(), [&](cir::IntAttr idxAttr) {
2820 return idxAttr.getSInt() != -1 && idxAttr.getUInt() > maxValidIndex;
2821 })) {
2822 return emitOpError() << ": index for __builtin_shufflevector must be "
2823 "less than the total number of vector elements";
2824 }
2825 return success();
2826}
2827
2828//===----------------------------------------------------------------------===//
2829// VecShuffleDynamicOp
2830//===----------------------------------------------------------------------===//
2831
2832OpFoldResult cir::VecShuffleDynamicOp::fold(FoldAdaptor adaptor) {
2833 mlir::Attribute vec = adaptor.getVec();
2834 mlir::Attribute indices = adaptor.getIndices();
2835 if (mlir::isa_and_nonnull<cir::ConstVectorAttr>(vec) &&
2836 mlir::isa_and_nonnull<cir::ConstVectorAttr>(indices)) {
2837 auto vecAttr = mlir::cast<cir::ConstVectorAttr>(vec);
2838 auto indicesAttr = mlir::cast<cir::ConstVectorAttr>(indices);
2839
2840 mlir::ArrayAttr vecElts = vecAttr.getElts();
2841 mlir::ArrayAttr indicesElts = indicesAttr.getElts();
2842
2843 const uint64_t numElements = vecElts.size();
2844
2846 elements.reserve(numElements);
2847
2848 const uint64_t maskBits = llvm::NextPowerOf2(numElements - 1) - 1;
2849 for (const auto &idxAttr : indicesElts.getAsRange<cir::IntAttr>()) {
2850 uint64_t idxValue = idxAttr.getUInt();
2851 uint64_t newIdx = idxValue & maskBits;
2852 elements.push_back(vecElts[newIdx]);
2853 }
2854
2855 return cir::ConstVectorAttr::get(
2856 getType(), mlir::ArrayAttr::get(getContext(), elements));
2857 }
2858
2859 return {};
2860}
2861
2862LogicalResult cir::VecShuffleDynamicOp::verify() {
2863 // The number of elements in the two input vectors must match.
2864 if (getVec().getType().getSize() !=
2865 mlir::cast<cir::VectorType>(getIndices().getType()).getSize()) {
2866 return emitOpError() << ": the number of elements in " << getVec().getType()
2867 << " and " << getIndices().getType() << " don't match";
2868 }
2869 return success();
2870}
2871
2872//===----------------------------------------------------------------------===//
2873// VecTernaryOp
2874//===----------------------------------------------------------------------===//
2875
2876LogicalResult cir::VecTernaryOp::verify() {
2877 // Verify that the condition operand has the same number of elements as the
2878 // other operands. (The automatic verification already checked that all
2879 // operands are vector types and that the second and third operands are the
2880 // same type.)
2881 if (getCond().getType().getSize() != getLhs().getType().getSize()) {
2882 return emitOpError() << ": the number of elements in "
2883 << getCond().getType() << " and " << getLhs().getType()
2884 << " don't match";
2885 }
2886 return success();
2887}
2888
2889OpFoldResult cir::VecTernaryOp::fold(FoldAdaptor adaptor) {
2890 mlir::Attribute cond = adaptor.getCond();
2891 mlir::Attribute lhs = adaptor.getLhs();
2892 mlir::Attribute rhs = adaptor.getRhs();
2893
2894 if (!mlir::isa_and_nonnull<cir::ConstVectorAttr>(cond) ||
2895 !mlir::isa_and_nonnull<cir::ConstVectorAttr>(lhs) ||
2896 !mlir::isa_and_nonnull<cir::ConstVectorAttr>(rhs))
2897 return {};
2898 auto condVec = mlir::cast<cir::ConstVectorAttr>(cond);
2899 auto lhsVec = mlir::cast<cir::ConstVectorAttr>(lhs);
2900 auto rhsVec = mlir::cast<cir::ConstVectorAttr>(rhs);
2901
2902 mlir::ArrayAttr condElts = condVec.getElts();
2903
2905 elements.reserve(condElts.size());
2906
2907 for (const auto &[idx, condAttr] :
2908 llvm::enumerate(condElts.getAsRange<cir::IntAttr>())) {
2909 if (condAttr.getSInt()) {
2910 elements.push_back(lhsVec.getElts()[idx]);
2911 } else {
2912 elements.push_back(rhsVec.getElts()[idx]);
2913 }
2914 }
2915
2916 cir::VectorType vecTy = getLhs().getType();
2917 return cir::ConstVectorAttr::get(
2918 vecTy, mlir::ArrayAttr::get(getContext(), elements));
2919}
2920
2921//===----------------------------------------------------------------------===//
2922// ComplexCreateOp
2923//===----------------------------------------------------------------------===//
2924
2925LogicalResult cir::ComplexCreateOp::verify() {
2926 if (getType().getElementType() != getReal().getType()) {
2927 emitOpError()
2928 << "operand type of cir.complex.create does not match its result type";
2929 return failure();
2930 }
2931
2932 return success();
2933}
2934
2935OpFoldResult cir::ComplexCreateOp::fold(FoldAdaptor adaptor) {
2936 mlir::Attribute real = adaptor.getReal();
2937 mlir::Attribute imag = adaptor.getImag();
2938 if (!real || !imag)
2939 return {};
2940
2941 // When both of real and imag are constants, we can fold the operation into an
2942 // `#cir.const_complex` operation.
2943 auto realAttr = mlir::cast<mlir::TypedAttr>(real);
2944 auto imagAttr = mlir::cast<mlir::TypedAttr>(imag);
2945 return cir::ConstComplexAttr::get(realAttr, imagAttr);
2946}
2947
2948//===----------------------------------------------------------------------===//
2949// ComplexRealOp
2950//===----------------------------------------------------------------------===//
2951
2952LogicalResult cir::ComplexRealOp::verify() {
2953 mlir::Type operandTy = getOperand().getType();
2954 if (auto complexOperandTy = mlir::dyn_cast<cir::ComplexType>(operandTy))
2955 operandTy = complexOperandTy.getElementType();
2956
2957 if (getType() != operandTy) {
2958 emitOpError() << ": result type does not match operand type";
2959 return failure();
2960 }
2961
2962 return success();
2963}
2964
2965OpFoldResult cir::ComplexRealOp::fold(FoldAdaptor adaptor) {
2966 if (!mlir::isa<cir::ComplexType>(getOperand().getType()))
2967 return nullptr;
2968
2969 if (auto complexCreateOp = getOperand().getDefiningOp<cir::ComplexCreateOp>())
2970 return complexCreateOp.getOperand(0);
2971
2972 auto complex =
2973 mlir::cast_if_present<cir::ConstComplexAttr>(adaptor.getOperand());
2974 return complex ? complex.getReal() : nullptr;
2975}
2976
2977//===----------------------------------------------------------------------===//
2978// ComplexImagOp
2979//===----------------------------------------------------------------------===//
2980
2981LogicalResult cir::ComplexImagOp::verify() {
2982 mlir::Type operandTy = getOperand().getType();
2983 if (auto complexOperandTy = mlir::dyn_cast<cir::ComplexType>(operandTy))
2984 operandTy = complexOperandTy.getElementType();
2985
2986 if (getType() != operandTy) {
2987 emitOpError() << ": result type does not match operand type";
2988 return failure();
2989 }
2990
2991 return success();
2992}
2993
2994OpFoldResult cir::ComplexImagOp::fold(FoldAdaptor adaptor) {
2995 if (!mlir::isa<cir::ComplexType>(getOperand().getType()))
2996 return nullptr;
2997
2998 if (auto complexCreateOp = getOperand().getDefiningOp<cir::ComplexCreateOp>())
2999 return complexCreateOp.getOperand(1);
3000
3001 auto complex =
3002 mlir::cast_if_present<cir::ConstComplexAttr>(adaptor.getOperand());
3003 return complex ? complex.getImag() : nullptr;
3004}
3005
3006//===----------------------------------------------------------------------===//
3007// ComplexRealPtrOp
3008//===----------------------------------------------------------------------===//
3009
3010LogicalResult cir::ComplexRealPtrOp::verify() {
3011 mlir::Type resultPointeeTy = getType().getPointee();
3012 cir::PointerType operandPtrTy = getOperand().getType();
3013 auto operandPointeeTy =
3014 mlir::cast<cir::ComplexType>(operandPtrTy.getPointee());
3015
3016 if (resultPointeeTy != operandPointeeTy.getElementType()) {
3017 return emitOpError() << ": result type does not match operand type";
3018 }
3019
3020 return success();
3021}
3022
3023//===----------------------------------------------------------------------===//
3024// ComplexImagPtrOp
3025//===----------------------------------------------------------------------===//
3026
3027LogicalResult cir::ComplexImagPtrOp::verify() {
3028 mlir::Type resultPointeeTy = getType().getPointee();
3029 cir::PointerType operandPtrTy = getOperand().getType();
3030 auto operandPointeeTy =
3031 mlir::cast<cir::ComplexType>(operandPtrTy.getPointee());
3032
3033 if (resultPointeeTy != operandPointeeTy.getElementType()) {
3034 return emitOpError()
3035 << "cir.complex.imag_ptr result type does not match operand type";
3036 }
3037 return success();
3038}
3039
3040//===----------------------------------------------------------------------===//
3041// Bit manipulation operations
3042//===----------------------------------------------------------------------===//
3043
3044static OpFoldResult
3045foldUnaryBitOp(mlir::Attribute inputAttr,
3046 llvm::function_ref<llvm::APInt(const llvm::APInt &)> func,
3047 bool poisonZero = false) {
3048 if (mlir::isa_and_present<cir::PoisonAttr>(inputAttr)) {
3049 // Propagate poison value
3050 return inputAttr;
3051 }
3052
3053 auto input = mlir::dyn_cast_if_present<IntAttr>(inputAttr);
3054 if (!input)
3055 return nullptr;
3056
3057 llvm::APInt inputValue = input.getValue();
3058 if (poisonZero && inputValue.isZero())
3059 return cir::PoisonAttr::get(input.getType());
3060
3061 llvm::APInt resultValue = func(inputValue);
3062 return IntAttr::get(input.getType(), resultValue);
3063}
3064
3065OpFoldResult BitClrsbOp::fold(FoldAdaptor adaptor) {
3066 return foldUnaryBitOp(adaptor.getInput(), [](const llvm::APInt &inputValue) {
3067 unsigned resultValue =
3068 inputValue.getBitWidth() - inputValue.getSignificantBits();
3069 return llvm::APInt(inputValue.getBitWidth(), resultValue);
3070 });
3071}
3072
3073OpFoldResult BitClzOp::fold(FoldAdaptor adaptor) {
3074 return foldUnaryBitOp(
3075 adaptor.getInput(),
3076 [](const llvm::APInt &inputValue) {
3077 unsigned resultValue = inputValue.countLeadingZeros();
3078 return llvm::APInt(inputValue.getBitWidth(), resultValue);
3079 },
3080 getPoisonZero());
3081}
3082
3083OpFoldResult BitCtzOp::fold(FoldAdaptor adaptor) {
3084 return foldUnaryBitOp(
3085 adaptor.getInput(),
3086 [](const llvm::APInt &inputValue) {
3087 return llvm::APInt(inputValue.getBitWidth(),
3088 inputValue.countTrailingZeros());
3089 },
3090 getPoisonZero());
3091}
3092
3093OpFoldResult BitFfsOp::fold(FoldAdaptor adaptor) {
3094 return foldUnaryBitOp(adaptor.getInput(), [](const llvm::APInt &inputValue) {
3095 unsigned trailingZeros = inputValue.countTrailingZeros();
3096 unsigned result =
3097 trailingZeros == inputValue.getBitWidth() ? 0 : trailingZeros + 1;
3098 return llvm::APInt(inputValue.getBitWidth(), result);
3099 });
3100}
3101
3102OpFoldResult BitParityOp::fold(FoldAdaptor adaptor) {
3103 return foldUnaryBitOp(adaptor.getInput(), [](const llvm::APInt &inputValue) {
3104 return llvm::APInt(inputValue.getBitWidth(), inputValue.popcount() % 2);
3105 });
3106}
3107
3108OpFoldResult BitPopcountOp::fold(FoldAdaptor adaptor) {
3109 return foldUnaryBitOp(adaptor.getInput(), [](const llvm::APInt &inputValue) {
3110 return llvm::APInt(inputValue.getBitWidth(), inputValue.popcount());
3111 });
3112}
3113
3114OpFoldResult BitReverseOp::fold(FoldAdaptor adaptor) {
3115 return foldUnaryBitOp(adaptor.getInput(), [](const llvm::APInt &inputValue) {
3116 return inputValue.reverseBits();
3117 });
3118}
3119
3120OpFoldResult ByteSwapOp::fold(FoldAdaptor adaptor) {
3121 return foldUnaryBitOp(adaptor.getInput(), [](const llvm::APInt &inputValue) {
3122 return inputValue.byteSwap();
3123 });
3124}
3125
3126OpFoldResult RotateOp::fold(FoldAdaptor adaptor) {
3127 if (mlir::isa_and_present<cir::PoisonAttr>(adaptor.getInput()) ||
3128 mlir::isa_and_present<cir::PoisonAttr>(adaptor.getAmount())) {
3129 // Propagate poison values
3130 return cir::PoisonAttr::get(getType());
3131 }
3132
3133 auto input = mlir::dyn_cast_if_present<IntAttr>(adaptor.getInput());
3134 auto amount = mlir::dyn_cast_if_present<IntAttr>(adaptor.getAmount());
3135 if (!input && !amount)
3136 return nullptr;
3137
3138 // We could fold cir.rotate even if one of its two operands is not a constant:
3139 // - `cir.rotate left/right %0, 0` could be folded into just %0 even if %0
3140 // is not a constant.
3141 // - `cir.rotate left/right 0/0b111...111, %0` could be folded into 0 or
3142 // 0b111...111 even if %0 is not a constant.
3143
3144 llvm::APInt inputValue;
3145 if (input) {
3146 inputValue = input.getValue();
3147 if (inputValue.isZero() || inputValue.isAllOnes()) {
3148 // An input value of all 0s or all 1s will not change after rotation
3149 return input;
3150 }
3151 }
3152
3153 uint64_t amountValue;
3154 if (amount) {
3155 amountValue = amount.getValue().urem(getInput().getType().getWidth());
3156 if (amountValue == 0) {
3157 // A shift amount of 0 will not change the input value
3158 return getInput();
3159 }
3160 }
3161
3162 if (!input || !amount)
3163 return nullptr;
3164
3165 assert(inputValue.getBitWidth() == getInput().getType().getWidth() &&
3166 "input value must have the same bit width as the input type");
3167
3168 llvm::APInt resultValue;
3169 if (isRotateLeft())
3170 resultValue = inputValue.rotl(amountValue);
3171 else
3172 resultValue = inputValue.rotr(amountValue);
3173
3174 return IntAttr::get(input.getContext(), input.getType(), resultValue);
3175}
3176
3177//===----------------------------------------------------------------------===//
3178// InlineAsmOp
3179//===----------------------------------------------------------------------===//
3180
3181void cir::InlineAsmOp::print(OpAsmPrinter &p) {
3182 p << '(' << getAsmFlavor() << ", ";
3183 p.increaseIndent();
3184 p.printNewline();
3185
3186 llvm::SmallVector<std::string, 3> names{"out", "in", "in_out"};
3187 auto *nameIt = names.begin();
3188 auto *attrIt = getOperandAttrs().begin();
3189
3190 for (mlir::OperandRange ops : getAsmOperands()) {
3191 p << *nameIt << " = ";
3192
3193 p << '[';
3194 llvm::interleaveComma(llvm::make_range(ops.begin(), ops.end()), p,
3195 [&](Value value) {
3196 p.printOperand(value);
3197 p << " : " << value.getType();
3198 if (*attrIt)
3199 p << " (maybe_memory)";
3200 attrIt++;
3201 });
3202 p << "],";
3203 p.printNewline();
3204 ++nameIt;
3205 }
3206
3207 p << "{";
3208 p.printString(getAsmString());
3209 p << " ";
3210 p.printString(getConstraints());
3211 p << "}";
3212 p.decreaseIndent();
3213 p << ')';
3214 if (getSideEffects())
3215 p << " side_effects";
3216
3217 std::array elidedAttrs{
3218 llvm::StringRef("asm_flavor"), llvm::StringRef("asm_string"),
3219 llvm::StringRef("constraints"), llvm::StringRef("operand_attrs"),
3220 llvm::StringRef("operands_segments"), llvm::StringRef("side_effects")};
3221 p.printOptionalAttrDict(getOperation()->getAttrs(), elidedAttrs);
3222
3223 if (auto v = getRes())
3224 p << " -> " << v.getType();
3225}
3226
3227void cir::InlineAsmOp::build(OpBuilder &odsBuilder, OperationState &odsState,
3228 ArrayRef<ValueRange> asmOperands,
3229 StringRef asmString, StringRef constraints,
3230 bool sideEffects, cir::AsmFlavor asmFlavor,
3231 ArrayRef<Attribute> operandAttrs) {
3232 // Set up the operands_segments for VariadicOfVariadic
3233 SmallVector<int32_t> segments;
3234 for (auto operandRange : asmOperands) {
3235 segments.push_back(operandRange.size());
3236 odsState.addOperands(operandRange);
3237 }
3238
3239 odsState.addAttribute(
3240 "operands_segments",
3241 DenseI32ArrayAttr::get(odsBuilder.getContext(), segments));
3242 odsState.addAttribute("asm_string", odsBuilder.getStringAttr(asmString));
3243 odsState.addAttribute("constraints", odsBuilder.getStringAttr(constraints));
3244 odsState.addAttribute("asm_flavor",
3245 AsmFlavorAttr::get(odsBuilder.getContext(), asmFlavor));
3246
3247 if (sideEffects)
3248 odsState.addAttribute("side_effects", odsBuilder.getUnitAttr());
3249
3250 odsState.addAttribute("operand_attrs", odsBuilder.getArrayAttr(operandAttrs));
3251}
3252
3253ParseResult cir::InlineAsmOp::parse(OpAsmParser &parser,
3254 OperationState &result) {
3256 llvm::SmallVector<int32_t> operandsGroupSizes;
3257 std::string asmString, constraints;
3258 Type resType;
3259 MLIRContext *ctxt = parser.getBuilder().getContext();
3260
3261 auto error = [&](const Twine &msg) -> LogicalResult {
3262 return parser.emitError(parser.getCurrentLocation(), msg);
3263 };
3264
3265 auto expected = [&](const std::string &c) {
3266 return error("expected '" + c + "'");
3267 };
3268
3269 if (parser.parseLParen().failed())
3270 return expected("(");
3271
3272 auto flavor = FieldParser<AsmFlavor, AsmFlavor>::parse(parser);
3273 if (failed(flavor))
3274 return error("Unknown AsmFlavor");
3275
3276 if (parser.parseComma().failed())
3277 return expected(",");
3278
3279 auto parseValue = [&](Value &v) {
3280 OpAsmParser::UnresolvedOperand op;
3281
3282 if (parser.parseOperand(op) || parser.parseColon())
3283 return error("can't parse operand");
3284
3285 Type typ;
3286 if (parser.parseType(typ).failed())
3287 return error("can't parse operand type");
3289 if (parser.resolveOperand(op, typ, tmp))
3290 return error("can't resolve operand");
3291 v = tmp[0];
3292 return mlir::success();
3293 };
3294
3295 auto parseOperands = [&](llvm::StringRef name) {
3296 if (parser.parseKeyword(name).failed())
3297 return error("expected " + name + " operands here");
3298 if (parser.parseEqual().failed())
3299 return expected("=");
3300 if (parser.parseLSquare().failed())
3301 return expected("[");
3302
3303 int size = 0;
3304 if (parser.parseOptionalRSquare().succeeded()) {
3305 operandsGroupSizes.push_back(size);
3306 if (parser.parseComma())
3307 return expected(",");
3308 return mlir::success();
3309 }
3310
3311 auto parseOperand = [&]() {
3312 Value val;
3313 if (parseValue(val).succeeded()) {
3314 result.operands.push_back(val);
3315 size++;
3316
3317 if (parser.parseOptionalLParen().failed()) {
3318 operandAttrs.push_back(mlir::Attribute());
3319 return mlir::success();
3320 }
3321
3322 if (parser.parseKeyword("maybe_memory").succeeded()) {
3323 operandAttrs.push_back(mlir::UnitAttr::get(ctxt));
3324 if (parser.parseRParen())
3325 return expected(")");
3326 return mlir::success();
3327 } else {
3328 return expected("maybe_memory");
3329 }
3330 }
3331 return mlir::failure();
3332 };
3333
3334 if (parser.parseCommaSeparatedList(parseOperand).failed())
3335 return mlir::failure();
3336
3337 if (parser.parseRSquare().failed() || parser.parseComma().failed())
3338 return expected("]");
3339 operandsGroupSizes.push_back(size);
3340 return mlir::success();
3341 };
3342
3343 if (parseOperands("out").failed() || parseOperands("in").failed() ||
3344 parseOperands("in_out").failed())
3345 return error("failed to parse operands");
3346
3347 if (parser.parseLBrace())
3348 return expected("{");
3349 if (parser.parseString(&asmString))
3350 return error("asm string parsing failed");
3351 if (parser.parseString(&constraints))
3352 return error("constraints string parsing failed");
3353 if (parser.parseRBrace())
3354 return expected("}");
3355 if (parser.parseRParen())
3356 return expected(")");
3357
3358 if (parser.parseOptionalKeyword("side_effects").succeeded())
3359 result.attributes.set("side_effects", UnitAttr::get(ctxt));
3360
3361 if (parser.parseOptionalArrow().succeeded() &&
3362 parser.parseType(resType).failed())
3363 return mlir::failure();
3364
3365 if (parser.parseOptionalAttrDict(result.attributes).failed())
3366 return mlir::failure();
3367
3368 result.attributes.set("asm_flavor", AsmFlavorAttr::get(ctxt, *flavor));
3369 result.attributes.set("asm_string", StringAttr::get(ctxt, asmString));
3370 result.attributes.set("constraints", StringAttr::get(ctxt, constraints));
3371 result.attributes.set("operand_attrs", ArrayAttr::get(ctxt, operandAttrs));
3372 result.getOrAddProperties<InlineAsmOp::Properties>().operands_segments =
3373 parser.getBuilder().getDenseI32ArrayAttr(operandsGroupSizes);
3374 if (resType)
3375 result.addTypes(TypeRange{resType});
3376
3377 return mlir::success();
3378}
3379
3380//===----------------------------------------------------------------------===//
3381// ThrowOp
3382//===----------------------------------------------------------------------===//
3383
3384mlir::LogicalResult cir::ThrowOp::verify() {
3385 // For the no-rethrow version, it must have at least the exception pointer.
3386 if (rethrows())
3387 return success();
3388
3389 if (getNumOperands() != 0) {
3390 if (getTypeInfo())
3391 return success();
3392 return emitOpError() << "'type_info' symbol attribute missing";
3393 }
3394
3395 return failure();
3396}
3397
3398//===----------------------------------------------------------------------===//
3399// AtomicFetchOp
3400//===----------------------------------------------------------------------===//
3401
3402LogicalResult cir::AtomicFetchOp::verify() {
3403 if (getBinop() != cir::AtomicFetchKind::Add &&
3404 getBinop() != cir::AtomicFetchKind::Sub &&
3405 getBinop() != cir::AtomicFetchKind::Max &&
3406 getBinop() != cir::AtomicFetchKind::Min &&
3407 !mlir::isa<cir::IntType>(getVal().getType()))
3408 return emitError("only atomic add, sub, max, and min operation could "
3409 "operate on floating-point values");
3410 return success();
3411}
3412
3413//===----------------------------------------------------------------------===//
3414// TypeInfoAttr
3415//===----------------------------------------------------------------------===//
3416
3417LogicalResult cir::TypeInfoAttr::verify(
3418 ::llvm::function_ref<::mlir::InFlightDiagnostic()> emitError,
3419 ::mlir::Type type, ::mlir::ArrayAttr typeInfoData) {
3420
3421 if (cir::ConstRecordAttr::verify(emitError, type, typeInfoData).failed())
3422 return failure();
3423
3424 return success();
3425}
3426
3427//===----------------------------------------------------------------------===//
3428// TryOp
3429//===----------------------------------------------------------------------===//
3430
3431void cir::TryOp::getSuccessorRegions(
3432 mlir::RegionBranchPoint point,
3434 // The `try` and the `catchers` region branch back to the parent operation.
3435 if (!point.isParent()) {
3436 regions.push_back(
3437 RegionSuccessor(getOperation(), getOperation()->getResults()));
3438 return;
3439 }
3440
3441 regions.push_back(mlir::RegionSuccessor(&getTryRegion()));
3442
3443 // TODO(CIR): If we know a target function never throws a specific type, we
3444 // can remove the catch handler.
3445 for (mlir::Region &handlerRegion : this->getHandlerRegions())
3446 regions.push_back(mlir::RegionSuccessor(&handlerRegion));
3447}
3448
3449static void
3450printTryHandlerRegions(mlir::OpAsmPrinter &printer, cir::TryOp op,
3451 mlir::MutableArrayRef<mlir::Region> handlerRegions,
3452 mlir::ArrayAttr handlerTypes) {
3453 if (!handlerTypes)
3454 return;
3455
3456 for (const auto [typeIdx, typeAttr] : llvm::enumerate(handlerTypes)) {
3457 if (typeIdx)
3458 printer << " ";
3459
3460 if (mlir::isa<cir::CatchAllAttr>(typeAttr)) {
3461 printer << "catch all ";
3462 } else if (mlir::isa<cir::UnwindAttr>(typeAttr)) {
3463 printer << "unwind ";
3464 } else {
3465 printer << "catch [type ";
3466 printer.printAttribute(typeAttr);
3467 printer << "] ";
3468 }
3469
3470 printer.printRegion(handlerRegions[typeIdx],
3471 /*printEntryBLockArgs=*/false,
3472 /*printBlockTerminators=*/true);
3473 }
3474}
3475
3476static mlir::ParseResult parseTryHandlerRegions(
3477 mlir::OpAsmParser &parser,
3478 llvm::SmallVectorImpl<std::unique_ptr<mlir::Region>> &handlerRegions,
3479 mlir::ArrayAttr &handlerTypes) {
3480
3481 auto parseCheckedCatcherRegion = [&]() -> mlir::ParseResult {
3482 handlerRegions.emplace_back(new mlir::Region);
3483
3484 mlir::Region &currRegion = *handlerRegions.back();
3485 mlir::SMLoc regionLoc = parser.getCurrentLocation();
3486 if (parser.parseRegion(currRegion)) {
3487 handlerRegions.clear();
3488 return failure();
3489 }
3490
3491 if (currRegion.empty())
3492 return parser.emitError(regionLoc, "handler region shall not be empty");
3493
3494 if (!(currRegion.back().mightHaveTerminator() &&
3495 currRegion.back().getTerminator()))
3496 return parser.emitError(
3497 regionLoc, "blocks are expected to be explicitly terminated");
3498
3499 return success();
3500 };
3501
3502 bool hasCatchAll = false;
3504 while (parser.parseOptionalKeyword("catch").succeeded()) {
3505 bool hasLSquare = parser.parseOptionalLSquare().succeeded();
3506
3507 llvm::StringRef attrStr;
3508 if (parser.parseOptionalKeyword(&attrStr, {"all", "type"}).failed())
3509 return parser.emitError(parser.getCurrentLocation(),
3510 "expected 'all' or 'type' keyword");
3511
3512 bool isCatchAll = attrStr == "all";
3513 if (isCatchAll) {
3514 if (hasCatchAll)
3515 return parser.emitError(parser.getCurrentLocation(),
3516 "can't have more than one catch all");
3517 hasCatchAll = true;
3518 }
3519
3520 mlir::Attribute exceptionRTTIAttr;
3521 if (!isCatchAll && parser.parseAttribute(exceptionRTTIAttr).failed())
3522 return parser.emitError(parser.getCurrentLocation(),
3523 "expected valid RTTI info attribute");
3524
3525 catcherAttrs.push_back(isCatchAll
3526 ? cir::CatchAllAttr::get(parser.getContext())
3527 : exceptionRTTIAttr);
3528
3529 if (hasLSquare && isCatchAll)
3530 return parser.emitError(parser.getCurrentLocation(),
3531 "catch all dosen't need RTTI info attribute");
3532
3533 if (hasLSquare && parser.parseRSquare().failed())
3534 return parser.emitError(parser.getCurrentLocation(),
3535 "expected `]` after RTTI info attribute");
3536
3537 if (parseCheckedCatcherRegion().failed())
3538 return mlir::failure();
3539 }
3540
3541 if (parser.parseOptionalKeyword("unwind").succeeded()) {
3542 if (hasCatchAll)
3543 return parser.emitError(parser.getCurrentLocation(),
3544 "unwind can't be used with catch all");
3545
3546 catcherAttrs.push_back(cir::UnwindAttr::get(parser.getContext()));
3547 if (parseCheckedCatcherRegion().failed())
3548 return mlir::failure();
3549 }
3550
3551 handlerTypes = parser.getBuilder().getArrayAttr(catcherAttrs);
3552 return mlir::success();
3553}
3554
3555//===----------------------------------------------------------------------===//
3556// EhTypeIdOp
3557//===----------------------------------------------------------------------===//
3558
3559LogicalResult
3560cir::EhTypeIdOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
3561 Operation *op = symbolTable.lookupNearestSymbolFrom(*this, getTypeSymAttr());
3562 if (!isa_and_nonnull<GlobalOp>(op))
3563 return emitOpError("'")
3564 << getTypeSym() << "' does not reference a valid cir.global";
3565 return success();
3566}
3567
3568//===----------------------------------------------------------------------===//
3569// TableGen'd op method definitions
3570//===----------------------------------------------------------------------===//
3571
3572#define GET_OP_CLASSES
3573#include "clang/CIR/Dialect/IR/CIROps.cpp.inc"
static const MemRegion * getRegion(const CallEvent &Call, const MutexDescriptor &Descriptor, bool IsLock)
static mlir::LogicalResult checkReturnAndFunction(cir::ReturnOp op, cir::FuncOp function)
static bool isBoolNot(cir::UnaryOp op)
static mlir::ParseResult parseCallCommon(mlir::OpAsmParser &parser, mlir::OperationState &result, bool hasDestinationBlocks=false)
static bool isIntOrBoolCast(cir::CastOp op)
static void printConstant(OpAsmPrinter &p, Attribute value)
static mlir::ParseResult parseOmittedTerminatorRegion(mlir::OpAsmParser &parser, mlir::Region &region)
ParseResult parseInlineKindAttr(OpAsmParser &parser, cir::InlineKindAttr &inlineKindAttr)
void printInlineKindAttr(OpAsmPrinter &p, cir::InlineKindAttr inlineKindAttr)
void printVisibilityAttr(OpAsmPrinter &printer, cir::VisibilityAttr &visibility)
static ParseResult parseSwitchFlatOpCases(OpAsmParser &parser, Type flagType, mlir::ArrayAttr &caseValues, SmallVectorImpl< Block * > &caseDestinations, SmallVectorImpl< llvm::SmallVector< OpAsmParser::UnresolvedOperand > > &caseOperands, SmallVectorImpl< llvm::SmallVector< Type > > &caseOperandTypes)
<cases> ::= [ (case (, case )* )?
static LogicalResult verifyCallCommInSymbolUses(mlir::Operation *op, SymbolTableCollection &symbolTable)
static LogicalResult ensureRegionTerm(OpAsmParser &parser, Region &region, SMLoc errLoc)
static ParseResult parseGlobalOpTypeAndInitialValue(OpAsmParser &parser, TypeAttr &typeAttr, Attribute &initialValueAttr, mlir::Region &ctorRegion, mlir::Region &dtorRegion)
void printIndirectBrOpSucessors(OpAsmPrinter &p, cir::IndirectBrOp op, Type flagType, SuccessorRange succs, OperandRangeRange succOperands, const TypeRangeRange &succOperandsTypes)
static OpFoldResult foldUnaryBitOp(mlir::Attribute inputAttr, llvm::function_ref< llvm::APInt(const llvm::APInt &)> func, bool poisonZero=false)
static llvm::StringRef getLinkageAttrNameString()
Returns the name used for the linkage attribute.
static RetTy parseOptionalCIRKeyword(AsmParser &parser, EnumTy defaultValue)
Parse an enum from the keyword, or default to the provided default value.
static void printSwitchFlatOpCases(OpAsmPrinter &p, cir::SwitchFlatOp op, Type flagType, mlir::ArrayAttr caseValues, SuccessorRange caseDestinations, OperandRangeRange caseOperands, const TypeRangeRange &caseOperandTypes)
static mlir::ParseResult parseTryCallDestinations(mlir::OpAsmParser &parser, mlir::OperationState &result)
static void printGlobalOpTypeAndInitialValue(OpAsmPrinter &p, cir::GlobalOp op, TypeAttr type, Attribute initAttr, mlir::Region &ctorRegion, mlir::Region &dtorRegion)
static ParseResult parseCIRKeyword(AsmParser &parser, RetTy &result)
Parse an enum from the keyword, return failure if the keyword is not found.
static Value tryFoldCastChain(cir::CastOp op)
void parseVisibilityAttr(OpAsmParser &parser, cir::VisibilityAttr &visibility)
static void printTryHandlerRegions(mlir::OpAsmPrinter &printer, cir::TryOp op, mlir::MutableArrayRef< mlir::Region > handlerRegions, mlir::ArrayAttr handlerTypes)
ParseResult parseIndirectBrOpSucessors(OpAsmParser &parser, Type &flagType, SmallVectorImpl< Block * > &succOperandBlocks, SmallVectorImpl< SmallVector< OpAsmParser::UnresolvedOperand > > &succOperands, SmallVectorImpl< SmallVector< Type > > &succOperandsTypes)
static bool omitRegionTerm(mlir::Region &r)
static void printCallCommon(mlir::Operation *op, mlir::FlatSymbolRefAttr calleeSym, mlir::Value indirectCallee, mlir::OpAsmPrinter &printer, bool isNothrow, cir::SideEffect sideEffect, mlir::Block *normalDest=nullptr, mlir::Block *unwindDest=nullptr)
static void printOmittedTerminatorRegion(mlir::OpAsmPrinter &printer, cir::ScopeOp &op, mlir::Region &region)
static ParseResult parseConstantValue(OpAsmParser &parser, mlir::Attribute &valueAttr)
static LogicalResult checkConstantTypes(mlir::Operation *op, mlir::Type opType, mlir::Attribute attrType)
static mlir::ParseResult parseTryHandlerRegions(mlir::OpAsmParser &parser, llvm::SmallVectorImpl< std::unique_ptr< mlir::Region > > &handlerRegions, mlir::ArrayAttr &handlerTypes)
#define REGISTER_ENUM_TYPE(Ty)
static int parseOptionalKeywordAlternative(AsmParser &parser, ArrayRef< llvm::StringRef > keywords)
llvm::function_ref< void(mlir::OpBuilder &, mlir::Location)> BuilderCallbackRef
Definition CIRDialect.h:37
llvm::function_ref< void( mlir::OpBuilder &, mlir::Location, mlir::OperationState &)> BuilderOpStateCallbackRef
Definition CIRDialect.h:39
static std::optional< NonLoc > getIndex(ProgramStateRef State, const ElementRegion *ER, CharKind CK)
static Decl::Kind getKind(const Decl *D)
TokenType getType() const
Returns the token's type, e.g.
__device__ __2f16 float c
void buildTerminatedBody(mlir::OpBuilder &builder, mlir::Location loc)
const internal::VariadicAllOfMatcher< Attr > attr
const AstTypeMatcher< RecordType > recordType
StringRef getName(const HeaderType T)
Definition HeaderFile.h:38
RangeSelector name(std::string ID)
Given a node with a "name", (like NamedDecl, DeclRefExpr, CxxCtorInitializer, and TypeLoc) selects th...
nullptr
This class represents a compute construct, representing a 'Kind' of ‘parallel’, 'serial',...
__DEVICE__ _Tp arg(const std::complex< _Tp > &__c)
static bool addressSpace()
static bool opCallCallConv()
static bool opScopeCleanupRegion()
static bool supportIFuncAttr()