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