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