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