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