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