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