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