clang 22.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
17
18#include "mlir/IR/DialectImplementation.h"
19#include "mlir/Interfaces/ControlFlowInterfaces.h"
20#include "mlir/Interfaces/FunctionImplementation.h"
21#include "mlir/Support/LLVM.h"
22
23#include "clang/CIR/Dialect/IR/CIROpsDialect.cpp.inc"
24#include "clang/CIR/Dialect/IR/CIROpsEnums.cpp.inc"
26#include "llvm/ADT/SetOperations.h"
27#include "llvm/ADT/SmallSet.h"
28#include "llvm/Support/LogicalResult.h"
29
30using namespace mlir;
31using namespace cir;
32
33//===----------------------------------------------------------------------===//
34// CIR Dialect
35//===----------------------------------------------------------------------===//
36namespace {
37struct CIROpAsmDialectInterface : public OpAsmDialectInterface {
38 using OpAsmDialectInterface::OpAsmDialectInterface;
39
40 AliasResult getAlias(Type type, raw_ostream &os) const final {
41 if (auto recordType = dyn_cast<cir::RecordType>(type)) {
42 StringAttr nameAttr = recordType.getName();
43 if (!nameAttr)
44 os << "rec_anon_" << recordType.getKindAsStr();
45 else
46 os << "rec_" << nameAttr.getValue();
47 return AliasResult::OverridableAlias;
48 }
49 if (auto intType = dyn_cast<cir::IntType>(type)) {
50 // We only provide alias for standard integer types (i.e. integer types
51 // whose width is a power of 2 and at least 8).
52 unsigned width = intType.getWidth();
53 if (width < 8 || !llvm::isPowerOf2_32(width))
54 return AliasResult::NoAlias;
55 os << intType.getAlias();
56 return AliasResult::OverridableAlias;
57 }
58 if (auto voidType = dyn_cast<cir::VoidType>(type)) {
59 os << voidType.getAlias();
60 return AliasResult::OverridableAlias;
61 }
62
63 return AliasResult::NoAlias;
64 }
65
66 AliasResult getAlias(Attribute attr, raw_ostream &os) const final {
67 if (auto boolAttr = mlir::dyn_cast<cir::BoolAttr>(attr)) {
68 os << (boolAttr.getValue() ? "true" : "false");
69 return AliasResult::FinalAlias;
70 }
71 if (auto bitfield = mlir::dyn_cast<cir::BitfieldInfoAttr>(attr)) {
72 os << "bfi_" << bitfield.getName().str();
73 return AliasResult::FinalAlias;
74 }
75 if (auto dynCastInfoAttr = mlir::dyn_cast<cir::DynamicCastInfoAttr>(attr)) {
76 os << dynCastInfoAttr.getAlias();
77 return AliasResult::FinalAlias;
78 }
79 return AliasResult::NoAlias;
80 }
81};
82} // namespace
83
84void cir::CIRDialect::initialize() {
85 registerTypes();
86 registerAttributes();
87 addOperations<
88#define GET_OP_LIST
89#include "clang/CIR/Dialect/IR/CIROps.cpp.inc"
90 >();
91 addInterfaces<CIROpAsmDialectInterface>();
92}
93
94Operation *cir::CIRDialect::materializeConstant(mlir::OpBuilder &builder,
95 mlir::Attribute value,
96 mlir::Type type,
97 mlir::Location loc) {
98 return cir::ConstantOp::create(builder, loc, type,
99 mlir::cast<mlir::TypedAttr>(value));
100}
101
102//===----------------------------------------------------------------------===//
103// Helpers
104//===----------------------------------------------------------------------===//
105
106// Parses one of the keywords provided in the list `keywords` and returns the
107// position of the parsed keyword in the list. If none of the keywords from the
108// list is parsed, returns -1.
109static int parseOptionalKeywordAlternative(AsmParser &parser,
110 ArrayRef<llvm::StringRef> keywords) {
111 for (auto en : llvm::enumerate(keywords)) {
112 if (succeeded(parser.parseOptionalKeyword(en.value())))
113 return en.index();
114 }
115 return -1;
116}
117
118namespace {
119template <typename Ty> struct EnumTraits {};
120
121#define REGISTER_ENUM_TYPE(Ty) \
122 template <> struct EnumTraits<cir::Ty> { \
123 static llvm::StringRef stringify(cir::Ty value) { \
124 return stringify##Ty(value); \
125 } \
126 static unsigned getMaxEnumVal() { return cir::getMaxEnumValFor##Ty(); } \
127 }
128
129REGISTER_ENUM_TYPE(GlobalLinkageKind);
130REGISTER_ENUM_TYPE(VisibilityKind);
131REGISTER_ENUM_TYPE(SideEffect);
132} // namespace
133
134/// Parse an enum from the keyword, or default to the provided default value.
135/// The return type is the enum type by default, unless overriden with the
136/// second template argument.
137template <typename EnumTy, typename RetTy = EnumTy>
138static RetTy parseOptionalCIRKeyword(AsmParser &parser, EnumTy defaultValue) {
140 for (unsigned i = 0, e = EnumTraits<EnumTy>::getMaxEnumVal(); i <= e; ++i)
141 names.push_back(EnumTraits<EnumTy>::stringify(static_cast<EnumTy>(i)));
142
143 int index = parseOptionalKeywordAlternative(parser, names);
144 if (index == -1)
145 return static_cast<RetTy>(defaultValue);
146 return static_cast<RetTy>(index);
147}
148
149/// Parse an enum from the keyword, return failure if the keyword is not found.
150template <typename EnumTy, typename RetTy = EnumTy>
151static ParseResult parseCIRKeyword(AsmParser &parser, RetTy &result) {
153 for (unsigned i = 0, e = EnumTraits<EnumTy>::getMaxEnumVal(); i <= e; ++i)
154 names.push_back(EnumTraits<EnumTy>::stringify(static_cast<EnumTy>(i)));
155
156 int index = parseOptionalKeywordAlternative(parser, names);
157 if (index == -1)
158 return failure();
159 result = static_cast<RetTy>(index);
160 return success();
161}
162
163// Check if a region's termination omission is valid and, if so, creates and
164// inserts the omitted terminator into the region.
165static LogicalResult ensureRegionTerm(OpAsmParser &parser, Region &region,
166 SMLoc errLoc) {
167 Location eLoc = parser.getEncodedSourceLoc(parser.getCurrentLocation());
168 OpBuilder builder(parser.getBuilder().getContext());
169
170 // Insert empty block in case the region is empty to ensure the terminator
171 // will be inserted
172 if (region.empty())
173 builder.createBlock(&region);
174
175 Block &block = region.back();
176 // Region is properly terminated: nothing to do.
177 if (!block.empty() && block.back().hasTrait<OpTrait::IsTerminator>())
178 return success();
179
180 // Check for invalid terminator omissions.
181 if (!region.hasOneBlock())
182 return parser.emitError(errLoc,
183 "multi-block region must not omit terminator");
184
185 // Terminator was omitted correctly: recreate it.
186 builder.setInsertionPointToEnd(&block);
187 cir::YieldOp::create(builder, eLoc);
188 return success();
189}
190
191// True if the region's terminator should be omitted.
192static bool omitRegionTerm(mlir::Region &r) {
193 const auto singleNonEmptyBlock = r.hasOneBlock() && !r.back().empty();
194 const auto yieldsNothing = [&r]() {
195 auto y = dyn_cast<cir::YieldOp>(r.back().getTerminator());
196 return y && y.getArgs().empty();
197 };
198 return singleNonEmptyBlock && yieldsNothing();
199}
200
201void printVisibilityAttr(OpAsmPrinter &printer,
202 cir::VisibilityAttr &visibility) {
203 switch (visibility.getValue()) {
204 case cir::VisibilityKind::Hidden:
205 printer << "hidden";
206 break;
207 case cir::VisibilityKind::Protected:
208 printer << "protected";
209 break;
210 case cir::VisibilityKind::Default:
211 break;
212 }
213}
214
215void parseVisibilityAttr(OpAsmParser &parser, cir::VisibilityAttr &visibility) {
216 cir::VisibilityKind visibilityKind =
217 parseOptionalCIRKeyword(parser, cir::VisibilityKind::Default);
218 visibility = cir::VisibilityAttr::get(parser.getContext(), visibilityKind);
219}
220
221//===----------------------------------------------------------------------===//
222// CIR Custom Parsers/Printers
223//===----------------------------------------------------------------------===//
224
225static mlir::ParseResult parseOmittedTerminatorRegion(mlir::OpAsmParser &parser,
226 mlir::Region &region) {
227 auto regionLoc = parser.getCurrentLocation();
228 if (parser.parseRegion(region))
229 return failure();
230 if (ensureRegionTerm(parser, region, regionLoc).failed())
231 return failure();
232 return success();
233}
234
235static void printOmittedTerminatorRegion(mlir::OpAsmPrinter &printer,
236 cir::ScopeOp &op,
237 mlir::Region &region) {
238 printer.printRegion(region,
239 /*printEntryBlockArgs=*/false,
240 /*printBlockTerminators=*/!omitRegionTerm(region));
241}
242
243//===----------------------------------------------------------------------===//
244// AllocaOp
245//===----------------------------------------------------------------------===//
246
247void cir::AllocaOp::build(mlir::OpBuilder &odsBuilder,
248 mlir::OperationState &odsState, mlir::Type addr,
249 mlir::Type allocaType, llvm::StringRef name,
250 mlir::IntegerAttr alignment) {
251 odsState.addAttribute(getAllocaTypeAttrName(odsState.name),
252 mlir::TypeAttr::get(allocaType));
253 odsState.addAttribute(getNameAttrName(odsState.name),
254 odsBuilder.getStringAttr(name));
255 if (alignment) {
256 odsState.addAttribute(getAlignmentAttrName(odsState.name), alignment);
257 }
258 odsState.addTypes(addr);
259}
260
261//===----------------------------------------------------------------------===//
262// BreakOp
263//===----------------------------------------------------------------------===//
264
265LogicalResult cir::BreakOp::verify() {
267 if (!getOperation()->getParentOfType<LoopOpInterface>() &&
268 !getOperation()->getParentOfType<SwitchOp>())
269 return emitOpError("must be within a loop");
270 return success();
271}
272
273//===----------------------------------------------------------------------===//
274// ConditionOp
275//===----------------------------------------------------------------------===//
276
277//===----------------------------------
278// BranchOpTerminatorInterface Methods
279//===----------------------------------
280
281void cir::ConditionOp::getSuccessorRegions(
282 ArrayRef<Attribute> operands, SmallVectorImpl<RegionSuccessor> &regions) {
283 // TODO(cir): The condition value may be folded to a constant, narrowing
284 // down its list of possible successors.
285
286 // Parent is a loop: condition may branch to the body or to the parent op.
287 if (auto loopOp = dyn_cast<LoopOpInterface>(getOperation()->getParentOp())) {
288 regions.emplace_back(&loopOp.getBody(), loopOp.getBody().getArguments());
289 regions.emplace_back(getOperation(), loopOp->getResults());
290 }
291
293}
294
295MutableOperandRange
296cir::ConditionOp::getMutableSuccessorOperands(RegionSuccessor point) {
297 // No values are yielded to the successor region.
298 return MutableOperandRange(getOperation(), 0, 0);
299}
300
301LogicalResult cir::ConditionOp::verify() {
303 if (!isa<LoopOpInterface>(getOperation()->getParentOp()))
304 return emitOpError("condition must be within a conditional region");
305 return success();
306}
307
308//===----------------------------------------------------------------------===//
309// ConstantOp
310//===----------------------------------------------------------------------===//
311
312static LogicalResult checkConstantTypes(mlir::Operation *op, mlir::Type opType,
313 mlir::Attribute attrType) {
314 if (isa<cir::ConstPtrAttr>(attrType)) {
315 if (!mlir::isa<cir::PointerType>(opType))
316 return op->emitOpError(
317 "pointer constant initializing a non-pointer type");
318 return success();
319 }
320
321 if (isa<cir::ZeroAttr>(attrType)) {
322 if (isa<cir::RecordType, cir::ArrayType, cir::VectorType, cir::ComplexType>(
323 opType))
324 return success();
325 return op->emitOpError(
326 "zero expects struct, array, vector, or complex type");
327 }
328
329 if (mlir::isa<cir::BoolAttr>(attrType)) {
330 if (!mlir::isa<cir::BoolType>(opType))
331 return op->emitOpError("result type (")
332 << opType << ") must be '!cir.bool' for '" << attrType << "'";
333 return success();
334 }
335
336 if (mlir::isa<cir::IntAttr, cir::FPAttr>(attrType)) {
337 auto at = cast<TypedAttr>(attrType);
338 if (at.getType() != opType) {
339 return op->emitOpError("result type (")
340 << opType << ") does not match value type (" << at.getType()
341 << ")";
342 }
343 return success();
344 }
345
346 if (mlir::isa<cir::ConstArrayAttr, cir::ConstVectorAttr,
347 cir::ConstComplexAttr, cir::ConstRecordAttr,
348 cir::GlobalViewAttr, cir::PoisonAttr, cir::TypeInfoAttr,
349 cir::VTableAttr>(attrType))
350 return success();
351
352 assert(isa<TypedAttr>(attrType) && "What else could we be looking at here?");
353 return op->emitOpError("global with type ")
354 << cast<TypedAttr>(attrType).getType() << " not yet supported";
355}
356
357LogicalResult cir::ConstantOp::verify() {
358 // ODS already generates checks to make sure the result type is valid. We just
359 // need to additionally check that the value's attribute type is consistent
360 // with the result type.
361 return checkConstantTypes(getOperation(), getType(), getValue());
362}
363
364OpFoldResult cir::ConstantOp::fold(FoldAdaptor /*adaptor*/) {
365 return getValue();
366}
367
368//===----------------------------------------------------------------------===//
369// ContinueOp
370//===----------------------------------------------------------------------===//
371
372LogicalResult cir::ContinueOp::verify() {
373 if (!getOperation()->getParentOfType<LoopOpInterface>())
374 return emitOpError("must be within a loop");
375 return success();
376}
377
378//===----------------------------------------------------------------------===//
379// CastOp
380//===----------------------------------------------------------------------===//
381
382LogicalResult cir::CastOp::verify() {
383 mlir::Type resType = getType();
384 mlir::Type srcType = getSrc().getType();
385
386 if (mlir::isa<cir::VectorType>(srcType) &&
387 mlir::isa<cir::VectorType>(resType)) {
388 // Use the element type of the vector to verify the cast kind. (Except for
389 // bitcast, see below.)
390 srcType = mlir::dyn_cast<cir::VectorType>(srcType).getElementType();
391 resType = mlir::dyn_cast<cir::VectorType>(resType).getElementType();
392 }
393
394 switch (getKind()) {
395 case cir::CastKind::int_to_bool: {
396 if (!mlir::isa<cir::BoolType>(resType))
397 return emitOpError() << "requires !cir.bool type for result";
398 if (!mlir::isa<cir::IntType>(srcType))
399 return emitOpError() << "requires !cir.int type for source";
400 return success();
401 }
402 case cir::CastKind::ptr_to_bool: {
403 if (!mlir::isa<cir::BoolType>(resType))
404 return emitOpError() << "requires !cir.bool type for result";
405 if (!mlir::isa<cir::PointerType>(srcType))
406 return emitOpError() << "requires !cir.ptr type for source";
407 return success();
408 }
409 case cir::CastKind::integral: {
410 if (!mlir::isa<cir::IntType>(resType))
411 return emitOpError() << "requires !cir.int type for result";
412 if (!mlir::isa<cir::IntType>(srcType))
413 return emitOpError() << "requires !cir.int type for source";
414 return success();
415 }
416 case cir::CastKind::array_to_ptrdecay: {
417 const auto arrayPtrTy = mlir::dyn_cast<cir::PointerType>(srcType);
418 const auto flatPtrTy = mlir::dyn_cast<cir::PointerType>(resType);
419 if (!arrayPtrTy || !flatPtrTy)
420 return emitOpError() << "requires !cir.ptr type for source and result";
421
422 // TODO(CIR): Make sure the AddrSpace of both types are equals
423 return success();
424 }
425 case cir::CastKind::bitcast: {
426 // Handle the pointer types first.
427 auto srcPtrTy = mlir::dyn_cast<cir::PointerType>(srcType);
428 auto resPtrTy = mlir::dyn_cast<cir::PointerType>(resType);
429
430 if (srcPtrTy && resPtrTy) {
431 return success();
432 }
433
434 return success();
435 }
436 case cir::CastKind::floating: {
437 if (!mlir::isa<cir::FPTypeInterface>(srcType) ||
438 !mlir::isa<cir::FPTypeInterface>(resType))
439 return emitOpError() << "requires !cir.float type for source and result";
440 return success();
441 }
442 case cir::CastKind::float_to_int: {
443 if (!mlir::isa<cir::FPTypeInterface>(srcType))
444 return emitOpError() << "requires !cir.float type for source";
445 if (!mlir::dyn_cast<cir::IntType>(resType))
446 return emitOpError() << "requires !cir.int type for result";
447 return success();
448 }
449 case cir::CastKind::int_to_ptr: {
450 if (!mlir::dyn_cast<cir::IntType>(srcType))
451 return emitOpError() << "requires !cir.int type for source";
452 if (!mlir::dyn_cast<cir::PointerType>(resType))
453 return emitOpError() << "requires !cir.ptr type for result";
454 return success();
455 }
456 case cir::CastKind::ptr_to_int: {
457 if (!mlir::dyn_cast<cir::PointerType>(srcType))
458 return emitOpError() << "requires !cir.ptr type for source";
459 if (!mlir::dyn_cast<cir::IntType>(resType))
460 return emitOpError() << "requires !cir.int type for result";
461 return success();
462 }
463 case cir::CastKind::float_to_bool: {
464 if (!mlir::isa<cir::FPTypeInterface>(srcType))
465 return emitOpError() << "requires !cir.float type for source";
466 if (!mlir::isa<cir::BoolType>(resType))
467 return emitOpError() << "requires !cir.bool type for result";
468 return success();
469 }
470 case cir::CastKind::bool_to_int: {
471 if (!mlir::isa<cir::BoolType>(srcType))
472 return emitOpError() << "requires !cir.bool type for source";
473 if (!mlir::isa<cir::IntType>(resType))
474 return emitOpError() << "requires !cir.int type for result";
475 return success();
476 }
477 case cir::CastKind::int_to_float: {
478 if (!mlir::isa<cir::IntType>(srcType))
479 return emitOpError() << "requires !cir.int type for source";
480 if (!mlir::isa<cir::FPTypeInterface>(resType))
481 return emitOpError() << "requires !cir.float type for result";
482 return success();
483 }
484 case cir::CastKind::bool_to_float: {
485 if (!mlir::isa<cir::BoolType>(srcType))
486 return emitOpError() << "requires !cir.bool type for source";
487 if (!mlir::isa<cir::FPTypeInterface>(resType))
488 return emitOpError() << "requires !cir.float type for result";
489 return success();
490 }
491 case cir::CastKind::address_space: {
492 auto srcPtrTy = mlir::dyn_cast<cir::PointerType>(srcType);
493 auto resPtrTy = mlir::dyn_cast<cir::PointerType>(resType);
494 if (!srcPtrTy || !resPtrTy)
495 return emitOpError() << "requires !cir.ptr type for source and result";
496 if (srcPtrTy.getPointee() != resPtrTy.getPointee())
497 return emitOpError() << "requires two types differ in addrspace only";
498 return success();
499 }
500 case cir::CastKind::float_to_complex: {
501 if (!mlir::isa<cir::FPTypeInterface>(srcType))
502 return emitOpError() << "requires !cir.float type for source";
503 auto resComplexTy = mlir::dyn_cast<cir::ComplexType>(resType);
504 if (!resComplexTy)
505 return emitOpError() << "requires !cir.complex type for result";
506 if (srcType != resComplexTy.getElementType())
507 return emitOpError() << "requires source type match result element type";
508 return success();
509 }
510 case cir::CastKind::int_to_complex: {
511 if (!mlir::isa<cir::IntType>(srcType))
512 return emitOpError() << "requires !cir.int type for source";
513 auto resComplexTy = mlir::dyn_cast<cir::ComplexType>(resType);
514 if (!resComplexTy)
515 return emitOpError() << "requires !cir.complex type for result";
516 if (srcType != resComplexTy.getElementType())
517 return emitOpError() << "requires source type match result element type";
518 return success();
519 }
520 case cir::CastKind::float_complex_to_real: {
521 auto srcComplexTy = mlir::dyn_cast<cir::ComplexType>(srcType);
522 if (!srcComplexTy)
523 return emitOpError() << "requires !cir.complex type for source";
524 if (!mlir::isa<cir::FPTypeInterface>(resType))
525 return emitOpError() << "requires !cir.float type for result";
526 if (srcComplexTy.getElementType() != resType)
527 return emitOpError() << "requires source element type match result type";
528 return success();
529 }
530 case cir::CastKind::int_complex_to_real: {
531 auto srcComplexTy = mlir::dyn_cast<cir::ComplexType>(srcType);
532 if (!srcComplexTy)
533 return emitOpError() << "requires !cir.complex type for source";
534 if (!mlir::isa<cir::IntType>(resType))
535 return emitOpError() << "requires !cir.int type for result";
536 if (srcComplexTy.getElementType() != resType)
537 return emitOpError() << "requires source element type match result type";
538 return success();
539 }
540 case cir::CastKind::float_complex_to_bool: {
541 auto srcComplexTy = mlir::dyn_cast<cir::ComplexType>(srcType);
542 if (!srcComplexTy || !srcComplexTy.isFloatingPointComplex())
543 return emitOpError()
544 << "requires floating point !cir.complex type for source";
545 if (!mlir::isa<cir::BoolType>(resType))
546 return emitOpError() << "requires !cir.bool type for result";
547 return success();
548 }
549 case cir::CastKind::int_complex_to_bool: {
550 auto srcComplexTy = mlir::dyn_cast<cir::ComplexType>(srcType);
551 if (!srcComplexTy || !srcComplexTy.isIntegerComplex())
552 return emitOpError()
553 << "requires floating point !cir.complex type for source";
554 if (!mlir::isa<cir::BoolType>(resType))
555 return emitOpError() << "requires !cir.bool type for result";
556 return success();
557 }
558 case cir::CastKind::float_complex: {
559 auto srcComplexTy = mlir::dyn_cast<cir::ComplexType>(srcType);
560 if (!srcComplexTy || !srcComplexTy.isFloatingPointComplex())
561 return emitOpError()
562 << "requires floating point !cir.complex type for source";
563 auto resComplexTy = mlir::dyn_cast<cir::ComplexType>(resType);
564 if (!resComplexTy || !resComplexTy.isFloatingPointComplex())
565 return emitOpError()
566 << "requires floating point !cir.complex type for result";
567 return success();
568 }
569 case cir::CastKind::float_complex_to_int_complex: {
570 auto srcComplexTy = mlir::dyn_cast<cir::ComplexType>(srcType);
571 if (!srcComplexTy || !srcComplexTy.isFloatingPointComplex())
572 return emitOpError()
573 << "requires floating point !cir.complex type for source";
574 auto resComplexTy = mlir::dyn_cast<cir::ComplexType>(resType);
575 if (!resComplexTy || !resComplexTy.isIntegerComplex())
576 return emitOpError() << "requires integer !cir.complex type for result";
577 return success();
578 }
579 case cir::CastKind::int_complex: {
580 auto srcComplexTy = mlir::dyn_cast<cir::ComplexType>(srcType);
581 if (!srcComplexTy || !srcComplexTy.isIntegerComplex())
582 return emitOpError() << "requires integer !cir.complex type for source";
583 auto resComplexTy = mlir::dyn_cast<cir::ComplexType>(resType);
584 if (!resComplexTy || !resComplexTy.isIntegerComplex())
585 return emitOpError() << "requires integer !cir.complex type for result";
586 return success();
587 }
588 case cir::CastKind::int_complex_to_float_complex: {
589 auto srcComplexTy = mlir::dyn_cast<cir::ComplexType>(srcType);
590 if (!srcComplexTy || !srcComplexTy.isIntegerComplex())
591 return emitOpError() << "requires integer !cir.complex type for source";
592 auto resComplexTy = mlir::dyn_cast<cir::ComplexType>(resType);
593 if (!resComplexTy || !resComplexTy.isFloatingPointComplex())
594 return emitOpError()
595 << "requires floating point !cir.complex type for result";
596 return success();
597 }
598 default:
599 llvm_unreachable("Unknown CastOp kind?");
600 }
601}
602
603static bool isIntOrBoolCast(cir::CastOp op) {
604 auto kind = op.getKind();
605 return kind == cir::CastKind::bool_to_int ||
606 kind == cir::CastKind::int_to_bool || kind == cir::CastKind::integral;
607}
608
609static Value tryFoldCastChain(cir::CastOp op) {
610 cir::CastOp head = op, tail = op;
611
612 while (op) {
613 if (!isIntOrBoolCast(op))
614 break;
615 head = op;
616 op = head.getSrc().getDefiningOp<cir::CastOp>();
617 }
618
619 if (head == tail)
620 return {};
621
622 // if bool_to_int -> ... -> int_to_bool: take the bool
623 // as we had it was before all casts
624 if (head.getKind() == cir::CastKind::bool_to_int &&
625 tail.getKind() == cir::CastKind::int_to_bool)
626 return head.getSrc();
627
628 // if int_to_bool -> ... -> int_to_bool: take the result
629 // of the first one, as no other casts (and ext casts as well)
630 // don't change the first result
631 if (head.getKind() == cir::CastKind::int_to_bool &&
632 tail.getKind() == cir::CastKind::int_to_bool)
633 return head.getResult();
634
635 return {};
636}
637
638OpFoldResult cir::CastOp::fold(FoldAdaptor adaptor) {
639 if (mlir::isa_and_present<cir::PoisonAttr>(adaptor.getSrc())) {
640 // Propagate poison value
641 return cir::PoisonAttr::get(getContext(), getType());
642 }
643
644 if (getSrc().getType() == getType()) {
645 switch (getKind()) {
646 case cir::CastKind::integral: {
647 // TODO: for sign differences, it's possible in certain conditions to
648 // create a new attribute that's capable of representing the source.
650 auto foldOrder = getSrc().getDefiningOp()->fold(foldResults);
651 if (foldOrder.succeeded() && mlir::isa<mlir::Attribute>(foldResults[0]))
652 return mlir::cast<mlir::Attribute>(foldResults[0]);
653 return {};
654 }
655 case cir::CastKind::bitcast:
656 case cir::CastKind::address_space:
657 case cir::CastKind::float_complex:
658 case cir::CastKind::int_complex: {
659 return getSrc();
660 }
661 default:
662 return {};
663 }
664 }
665 return tryFoldCastChain(*this);
666}
667
668//===----------------------------------------------------------------------===//
669// CallOp
670//===----------------------------------------------------------------------===//
671
672mlir::OperandRange cir::CallOp::getArgOperands() {
673 if (isIndirect())
674 return getArgs().drop_front(1);
675 return getArgs();
676}
677
678mlir::MutableOperandRange cir::CallOp::getArgOperandsMutable() {
679 mlir::MutableOperandRange args = getArgsMutable();
680 if (isIndirect())
681 return args.slice(1, args.size() - 1);
682 return args;
683}
684
685mlir::Value cir::CallOp::getIndirectCall() {
686 assert(isIndirect());
687 return getOperand(0);
688}
689
690/// Return the operand at index 'i'.
691Value cir::CallOp::getArgOperand(unsigned i) {
692 if (isIndirect())
693 ++i;
694 return getOperand(i);
695}
696
697/// Return the number of operands.
698unsigned cir::CallOp::getNumArgOperands() {
699 if (isIndirect())
700 return this->getOperation()->getNumOperands() - 1;
701 return this->getOperation()->getNumOperands();
702}
703
704static mlir::ParseResult parseCallCommon(mlir::OpAsmParser &parser,
705 mlir::OperationState &result) {
707 llvm::SMLoc opsLoc;
708 mlir::FlatSymbolRefAttr calleeAttr;
709 llvm::ArrayRef<mlir::Type> allResultTypes;
710
711 // If we cannot parse a string callee, it means this is an indirect call.
712 if (!parser
713 .parseOptionalAttribute(calleeAttr, CIRDialect::getCalleeAttrName(),
714 result.attributes)
715 .has_value()) {
716 OpAsmParser::UnresolvedOperand indirectVal;
717 // Do not resolve right now, since we need to figure out the type
718 if (parser.parseOperand(indirectVal).failed())
719 return failure();
720 ops.push_back(indirectVal);
721 }
722
723 if (parser.parseLParen())
724 return mlir::failure();
725
726 opsLoc = parser.getCurrentLocation();
727 if (parser.parseOperandList(ops))
728 return mlir::failure();
729 if (parser.parseRParen())
730 return mlir::failure();
731
732 if (parser.parseOptionalKeyword("nothrow").succeeded())
733 result.addAttribute(CIRDialect::getNoThrowAttrName(),
734 mlir::UnitAttr::get(parser.getContext()));
735
736 if (parser.parseOptionalKeyword("side_effect").succeeded()) {
737 if (parser.parseLParen().failed())
738 return failure();
739 cir::SideEffect sideEffect;
740 if (parseCIRKeyword<cir::SideEffect>(parser, sideEffect).failed())
741 return failure();
742 if (parser.parseRParen().failed())
743 return failure();
744 auto attr = cir::SideEffectAttr::get(parser.getContext(), sideEffect);
745 result.addAttribute(CIRDialect::getSideEffectAttrName(), attr);
746 }
747
748 if (parser.parseOptionalAttrDict(result.attributes))
749 return ::mlir::failure();
750
751 if (parser.parseColon())
752 return ::mlir::failure();
753
754 mlir::FunctionType opsFnTy;
755 if (parser.parseType(opsFnTy))
756 return mlir::failure();
757
758 allResultTypes = opsFnTy.getResults();
759 result.addTypes(allResultTypes);
760
761 if (parser.resolveOperands(ops, opsFnTy.getInputs(), opsLoc, result.operands))
762 return mlir::failure();
763
764 return mlir::success();
765}
766
767static void printCallCommon(mlir::Operation *op,
768 mlir::FlatSymbolRefAttr calleeSym,
769 mlir::Value indirectCallee,
770 mlir::OpAsmPrinter &printer, bool isNothrow,
771 cir::SideEffect sideEffect) {
772 printer << ' ';
773
774 auto callLikeOp = mlir::cast<cir::CIRCallOpInterface>(op);
775 auto ops = callLikeOp.getArgOperands();
776
777 if (calleeSym) {
778 // Direct calls
779 printer.printAttributeWithoutType(calleeSym);
780 } else {
781 // Indirect calls
782 assert(indirectCallee);
783 printer << indirectCallee;
784 }
785 printer << "(" << ops << ")";
786
787 if (isNothrow)
788 printer << " nothrow";
789
790 if (sideEffect != cir::SideEffect::All) {
791 printer << " side_effect(";
792 printer << stringifySideEffect(sideEffect);
793 printer << ")";
794 }
795
796 printer.printOptionalAttrDict(op->getAttrs(),
797 {CIRDialect::getCalleeAttrName(),
798 CIRDialect::getNoThrowAttrName(),
799 CIRDialect::getSideEffectAttrName()});
800
801 printer << " : ";
802 printer.printFunctionalType(op->getOperands().getTypes(),
803 op->getResultTypes());
804}
805
806mlir::ParseResult cir::CallOp::parse(mlir::OpAsmParser &parser,
807 mlir::OperationState &result) {
808 return parseCallCommon(parser, result);
809}
810
811void cir::CallOp::print(mlir::OpAsmPrinter &p) {
812 mlir::Value indirectCallee = isIndirect() ? getIndirectCall() : nullptr;
813 cir::SideEffect sideEffect = getSideEffect();
814 printCallCommon(*this, getCalleeAttr(), indirectCallee, p, getNothrow(),
815 sideEffect);
816}
817
818static LogicalResult
819verifyCallCommInSymbolUses(mlir::Operation *op,
820 SymbolTableCollection &symbolTable) {
821 auto fnAttr =
822 op->getAttrOfType<FlatSymbolRefAttr>(CIRDialect::getCalleeAttrName());
823 if (!fnAttr) {
824 // This is an indirect call, thus we don't have to check the symbol uses.
825 return mlir::success();
826 }
827
828 auto fn = symbolTable.lookupNearestSymbolFrom<cir::FuncOp>(op, fnAttr);
829 if (!fn)
830 return op->emitOpError() << "'" << fnAttr.getValue()
831 << "' does not reference a valid function";
832
833 auto callIf = dyn_cast<cir::CIRCallOpInterface>(op);
834 assert(callIf && "expected CIR call interface to be always available");
835
836 // Verify that the operand and result types match the callee. Note that
837 // argument-checking is disabled for functions without a prototype.
838 auto fnType = fn.getFunctionType();
839 if (!fn.getNoProto()) {
840 unsigned numCallOperands = callIf.getNumArgOperands();
841 unsigned numFnOpOperands = fnType.getNumInputs();
842
843 if (!fnType.isVarArg() && numCallOperands != numFnOpOperands)
844 return op->emitOpError("incorrect number of operands for callee");
845 if (fnType.isVarArg() && numCallOperands < numFnOpOperands)
846 return op->emitOpError("too few operands for callee");
847
848 for (unsigned i = 0, e = numFnOpOperands; i != e; ++i)
849 if (callIf.getArgOperand(i).getType() != fnType.getInput(i))
850 return op->emitOpError("operand type mismatch: expected operand type ")
851 << fnType.getInput(i) << ", but provided "
852 << op->getOperand(i).getType() << " for operand number " << i;
853 }
854
856
857 // Void function must not return any results.
858 if (fnType.hasVoidReturn() && op->getNumResults() != 0)
859 return op->emitOpError("callee returns void but call has results");
860
861 // Non-void function calls must return exactly one result.
862 if (!fnType.hasVoidReturn() && op->getNumResults() != 1)
863 return op->emitOpError("incorrect number of results for callee");
864
865 // Parent function and return value types must match.
866 if (!fnType.hasVoidReturn() &&
867 op->getResultTypes().front() != fnType.getReturnType()) {
868 return op->emitOpError("result type mismatch: expected ")
869 << fnType.getReturnType() << ", but provided "
870 << op->getResult(0).getType();
871 }
872
873 return mlir::success();
874}
875
876LogicalResult
877cir::CallOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
878 return verifyCallCommInSymbolUses(*this, symbolTable);
879}
880
881//===----------------------------------------------------------------------===//
882// ReturnOp
883//===----------------------------------------------------------------------===//
884
885static mlir::LogicalResult checkReturnAndFunction(cir::ReturnOp op,
886 cir::FuncOp function) {
887 // ReturnOps currently only have a single optional operand.
888 if (op.getNumOperands() > 1)
889 return op.emitOpError() << "expects at most 1 return operand";
890
891 // Ensure returned type matches the function signature.
892 auto expectedTy = function.getFunctionType().getReturnType();
893 auto actualTy =
894 (op.getNumOperands() == 0 ? cir::VoidType::get(op.getContext())
895 : op.getOperand(0).getType());
896 if (actualTy != expectedTy)
897 return op.emitOpError() << "returns " << actualTy
898 << " but enclosing function returns " << expectedTy;
899
900 return mlir::success();
901}
902
903mlir::LogicalResult cir::ReturnOp::verify() {
904 // Returns can be present in multiple different scopes, get the
905 // wrapping function and start from there.
906 auto *fnOp = getOperation()->getParentOp();
907 while (!isa<cir::FuncOp>(fnOp))
908 fnOp = fnOp->getParentOp();
909
910 // Make sure return types match function return type.
911 if (checkReturnAndFunction(*this, cast<cir::FuncOp>(fnOp)).failed())
912 return failure();
913
914 return success();
915}
916
917//===----------------------------------------------------------------------===//
918// IfOp
919//===----------------------------------------------------------------------===//
920
921ParseResult cir::IfOp::parse(OpAsmParser &parser, OperationState &result) {
922 // create the regions for 'then'.
923 result.regions.reserve(2);
924 Region *thenRegion = result.addRegion();
925 Region *elseRegion = result.addRegion();
926
927 mlir::Builder &builder = parser.getBuilder();
928 OpAsmParser::UnresolvedOperand cond;
929 Type boolType = cir::BoolType::get(builder.getContext());
930
931 if (parser.parseOperand(cond) ||
932 parser.resolveOperand(cond, boolType, result.operands))
933 return failure();
934
935 // Parse 'then' region.
936 mlir::SMLoc parseThenLoc = parser.getCurrentLocation();
937 if (parser.parseRegion(*thenRegion, /*arguments=*/{}, /*argTypes=*/{}))
938 return failure();
939
940 if (ensureRegionTerm(parser, *thenRegion, parseThenLoc).failed())
941 return failure();
942
943 // If we find an 'else' keyword, parse the 'else' region.
944 if (!parser.parseOptionalKeyword("else")) {
945 mlir::SMLoc parseElseLoc = parser.getCurrentLocation();
946 if (parser.parseRegion(*elseRegion, /*arguments=*/{}, /*argTypes=*/{}))
947 return failure();
948 if (ensureRegionTerm(parser, *elseRegion, parseElseLoc).failed())
949 return failure();
950 }
951
952 // Parse the optional attribute list.
953 if (parser.parseOptionalAttrDict(result.attributes))
954 return failure();
955 return success();
956}
957
958void cir::IfOp::print(OpAsmPrinter &p) {
959 p << " " << getCondition() << " ";
960 mlir::Region &thenRegion = this->getThenRegion();
961 p.printRegion(thenRegion,
962 /*printEntryBlockArgs=*/false,
963 /*printBlockTerminators=*/!omitRegionTerm(thenRegion));
964
965 // Print the 'else' regions if it exists and has a block.
966 mlir::Region &elseRegion = this->getElseRegion();
967 if (!elseRegion.empty()) {
968 p << " else ";
969 p.printRegion(elseRegion,
970 /*printEntryBlockArgs=*/false,
971 /*printBlockTerminators=*/!omitRegionTerm(elseRegion));
972 }
973
974 p.printOptionalAttrDict(getOperation()->getAttrs());
975}
976
977/// Default callback for IfOp builders.
978void cir::buildTerminatedBody(OpBuilder &builder, Location loc) {
979 // add cir.yield to end of the block
980 cir::YieldOp::create(builder, loc);
981}
982
983/// Given the region at `index`, or the parent operation if `index` is None,
984/// return the successor regions. These are the regions that may be selected
985/// during the flow of control. `operands` is a set of optional attributes that
986/// correspond to a constant value for each operand, or null if that operand is
987/// not a constant.
988void cir::IfOp::getSuccessorRegions(mlir::RegionBranchPoint point,
989 SmallVectorImpl<RegionSuccessor> &regions) {
990 // The `then` and the `else` region branch back to the parent operation.
991 if (!point.isParent()) {
992 regions.push_back(
993 RegionSuccessor(getOperation(), getOperation()->getResults()));
994 return;
995 }
996
997 // Don't consider the else region if it is empty.
998 Region *elseRegion = &this->getElseRegion();
999 if (elseRegion->empty())
1000 elseRegion = nullptr;
1001
1002 // If the condition isn't constant, both regions may be executed.
1003 regions.push_back(RegionSuccessor(&getThenRegion()));
1004 // If the else region does not exist, it is not a viable successor.
1005 if (elseRegion)
1006 regions.push_back(RegionSuccessor(elseRegion));
1007
1008 return;
1009}
1010
1011void cir::IfOp::build(OpBuilder &builder, OperationState &result, Value cond,
1012 bool withElseRegion, BuilderCallbackRef thenBuilder,
1013 BuilderCallbackRef elseBuilder) {
1014 assert(thenBuilder && "the builder callback for 'then' must be present");
1015 result.addOperands(cond);
1016
1017 OpBuilder::InsertionGuard guard(builder);
1018 Region *thenRegion = result.addRegion();
1019 builder.createBlock(thenRegion);
1020 thenBuilder(builder, result.location);
1021
1022 Region *elseRegion = result.addRegion();
1023 if (!withElseRegion)
1024 return;
1025
1026 builder.createBlock(elseRegion);
1027 elseBuilder(builder, result.location);
1028}
1029
1030//===----------------------------------------------------------------------===//
1031// ScopeOp
1032//===----------------------------------------------------------------------===//
1033
1034/// Given the region at `index`, or the parent operation if `index` is None,
1035/// return the successor regions. These are the regions that may be selected
1036/// during the flow of control. `operands` is a set of optional attributes
1037/// that correspond to a constant value for each operand, or null if that
1038/// operand is not a constant.
1039void cir::ScopeOp::getSuccessorRegions(
1040 mlir::RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &regions) {
1041 // The only region always branch back to the parent operation.
1042 if (!point.isParent()) {
1043 regions.push_back(RegionSuccessor(getOperation(), getODSResults(0)));
1044 return;
1045 }
1046
1047 // If the condition isn't constant, both regions may be executed.
1048 regions.push_back(RegionSuccessor(&getScopeRegion()));
1049}
1050
1051void cir::ScopeOp::build(
1052 OpBuilder &builder, OperationState &result,
1053 function_ref<void(OpBuilder &, Type &, Location)> scopeBuilder) {
1054 assert(scopeBuilder && "the builder callback for 'then' must be present");
1055
1056 OpBuilder::InsertionGuard guard(builder);
1057 Region *scopeRegion = result.addRegion();
1058 builder.createBlock(scopeRegion);
1060
1061 mlir::Type yieldTy;
1062 scopeBuilder(builder, yieldTy, result.location);
1063
1064 if (yieldTy)
1065 result.addTypes(TypeRange{yieldTy});
1066}
1067
1068void cir::ScopeOp::build(
1069 OpBuilder &builder, OperationState &result,
1070 function_ref<void(OpBuilder &, Location)> scopeBuilder) {
1071 assert(scopeBuilder && "the builder callback for 'then' must be present");
1072 OpBuilder::InsertionGuard guard(builder);
1073 Region *scopeRegion = result.addRegion();
1074 builder.createBlock(scopeRegion);
1076 scopeBuilder(builder, result.location);
1077}
1078
1079LogicalResult cir::ScopeOp::verify() {
1080 if (getRegion().empty()) {
1081 return emitOpError() << "cir.scope must not be empty since it should "
1082 "include at least an implicit cir.yield ";
1083 }
1084
1085 mlir::Block &lastBlock = getRegion().back();
1086 if (lastBlock.empty() || !lastBlock.mightHaveTerminator() ||
1087 !lastBlock.getTerminator()->hasTrait<OpTrait::IsTerminator>())
1088 return emitOpError() << "last block of cir.scope must be terminated";
1089 return success();
1090}
1091
1092//===----------------------------------------------------------------------===//
1093// BrOp
1094//===----------------------------------------------------------------------===//
1095
1096mlir::SuccessorOperands cir::BrOp::getSuccessorOperands(unsigned index) {
1097 assert(index == 0 && "invalid successor index");
1098 return mlir::SuccessorOperands(getDestOperandsMutable());
1099}
1100
1101Block *cir::BrOp::getSuccessorForOperands(ArrayRef<Attribute>) {
1102 return getDest();
1103}
1104
1105//===----------------------------------------------------------------------===//
1106// BrCondOp
1107//===----------------------------------------------------------------------===//
1108
1109mlir::SuccessorOperands cir::BrCondOp::getSuccessorOperands(unsigned index) {
1110 assert(index < getNumSuccessors() && "invalid successor index");
1111 return SuccessorOperands(index == 0 ? getDestOperandsTrueMutable()
1112 : getDestOperandsFalseMutable());
1113}
1114
1115Block *cir::BrCondOp::getSuccessorForOperands(ArrayRef<Attribute> operands) {
1116 if (IntegerAttr condAttr = dyn_cast_if_present<IntegerAttr>(operands.front()))
1117 return condAttr.getValue().isOne() ? getDestTrue() : getDestFalse();
1118 return nullptr;
1119}
1120
1121//===----------------------------------------------------------------------===//
1122// CaseOp
1123//===----------------------------------------------------------------------===//
1124
1125void cir::CaseOp::getSuccessorRegions(
1126 mlir::RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &regions) {
1127 if (!point.isParent()) {
1128 regions.push_back(
1129 RegionSuccessor(getOperation(), getOperation()->getResults()));
1130 return;
1131 }
1132 regions.push_back(RegionSuccessor(&getCaseRegion()));
1133}
1134
1135void cir::CaseOp::build(OpBuilder &builder, OperationState &result,
1136 ArrayAttr value, CaseOpKind kind,
1137 OpBuilder::InsertPoint &insertPoint) {
1138 OpBuilder::InsertionGuard guardSwitch(builder);
1139 result.addAttribute("value", value);
1140 result.getOrAddProperties<Properties>().kind =
1141 cir::CaseOpKindAttr::get(builder.getContext(), kind);
1142 Region *caseRegion = result.addRegion();
1143 builder.createBlock(caseRegion);
1144
1145 insertPoint = builder.saveInsertionPoint();
1146}
1147
1148//===----------------------------------------------------------------------===//
1149// SwitchOp
1150//===----------------------------------------------------------------------===//
1151
1152static ParseResult parseSwitchOp(OpAsmParser &parser, mlir::Region &regions,
1153 mlir::OpAsmParser::UnresolvedOperand &cond,
1154 mlir::Type &condType) {
1155 cir::IntType intCondType;
1156
1157 if (parser.parseLParen())
1158 return mlir::failure();
1159
1160 if (parser.parseOperand(cond))
1161 return mlir::failure();
1162 if (parser.parseColon())
1163 return mlir::failure();
1164 if (parser.parseCustomTypeWithFallback(intCondType))
1165 return mlir::failure();
1166 condType = intCondType;
1167
1168 if (parser.parseRParen())
1169 return mlir::failure();
1170 if (parser.parseRegion(regions, /*arguments=*/{}, /*argTypes=*/{}))
1171 return failure();
1172
1173 return mlir::success();
1174}
1175
1176static void printSwitchOp(OpAsmPrinter &p, cir::SwitchOp op,
1177 mlir::Region &bodyRegion, mlir::Value condition,
1178 mlir::Type condType) {
1179 p << "(";
1180 p << condition;
1181 p << " : ";
1182 p.printStrippedAttrOrType(condType);
1183 p << ")";
1184
1185 p << ' ';
1186 p.printRegion(bodyRegion, /*printEntryBlockArgs=*/false,
1187 /*printBlockTerminators=*/true);
1188}
1189
1190void cir::SwitchOp::getSuccessorRegions(
1191 mlir::RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &region) {
1192 if (!point.isParent()) {
1193 region.push_back(
1194 RegionSuccessor(getOperation(), getOperation()->getResults()));
1195 return;
1196 }
1197
1198 region.push_back(RegionSuccessor(&getBody()));
1199}
1200
1201void cir::SwitchOp::build(OpBuilder &builder, OperationState &result,
1202 Value cond, BuilderOpStateCallbackRef switchBuilder) {
1203 assert(switchBuilder && "the builder callback for regions must be present");
1204 OpBuilder::InsertionGuard guardSwitch(builder);
1205 Region *switchRegion = result.addRegion();
1206 builder.createBlock(switchRegion);
1207 result.addOperands({cond});
1208 switchBuilder(builder, result.location, result);
1209}
1210
1211void cir::SwitchOp::collectCases(llvm::SmallVectorImpl<CaseOp> &cases) {
1212 walk<mlir::WalkOrder::PreOrder>([&](mlir::Operation *op) {
1213 // Don't walk in nested switch op.
1214 if (isa<cir::SwitchOp>(op) && op != *this)
1215 return WalkResult::skip();
1216
1217 if (auto caseOp = dyn_cast<cir::CaseOp>(op))
1218 cases.push_back(caseOp);
1219
1220 return WalkResult::advance();
1221 });
1222}
1223
1224bool cir::SwitchOp::isSimpleForm(llvm::SmallVectorImpl<CaseOp> &cases) {
1225 collectCases(cases);
1226
1227 if (getBody().empty())
1228 return false;
1229
1230 if (!isa<YieldOp>(getBody().front().back()))
1231 return false;
1232
1233 if (!llvm::all_of(getBody().front(),
1234 [](Operation &op) { return isa<CaseOp, YieldOp>(op); }))
1235 return false;
1236
1237 return llvm::all_of(cases, [this](CaseOp op) {
1238 return op->getParentOfType<SwitchOp>() == *this;
1239 });
1240}
1241
1242//===----------------------------------------------------------------------===//
1243// SwitchFlatOp
1244//===----------------------------------------------------------------------===//
1245
1246void cir::SwitchFlatOp::build(OpBuilder &builder, OperationState &result,
1247 Value value, Block *defaultDestination,
1248 ValueRange defaultOperands,
1249 ArrayRef<APInt> caseValues,
1250 BlockRange caseDestinations,
1251 ArrayRef<ValueRange> caseOperands) {
1252
1253 std::vector<mlir::Attribute> caseValuesAttrs;
1254 for (const APInt &val : caseValues)
1255 caseValuesAttrs.push_back(cir::IntAttr::get(value.getType(), val));
1256 mlir::ArrayAttr attrs = ArrayAttr::get(builder.getContext(), caseValuesAttrs);
1257
1258 build(builder, result, value, defaultOperands, caseOperands, attrs,
1259 defaultDestination, caseDestinations);
1260}
1261
1262/// <cases> ::= `[` (case (`,` case )* )? `]`
1263/// <case> ::= integer `:` bb-id (`(` ssa-use-and-type-list `)`)?
1264static ParseResult parseSwitchFlatOpCases(
1265 OpAsmParser &parser, Type flagType, mlir::ArrayAttr &caseValues,
1266 SmallVectorImpl<Block *> &caseDestinations,
1268 &caseOperands,
1269 SmallVectorImpl<llvm::SmallVector<Type>> &caseOperandTypes) {
1270 if (failed(parser.parseLSquare()))
1271 return failure();
1272 if (succeeded(parser.parseOptionalRSquare()))
1273 return success();
1275
1276 auto parseCase = [&]() {
1277 int64_t value = 0;
1278 if (failed(parser.parseInteger(value)))
1279 return failure();
1280
1281 values.push_back(cir::IntAttr::get(flagType, value));
1282
1283 Block *destination;
1285 llvm::SmallVector<Type> operandTypes;
1286 if (parser.parseColon() || parser.parseSuccessor(destination))
1287 return failure();
1288 if (!parser.parseOptionalLParen()) {
1289 if (parser.parseOperandList(operands, OpAsmParser::Delimiter::None,
1290 /*allowResultNumber=*/false) ||
1291 parser.parseColonTypeList(operandTypes) || parser.parseRParen())
1292 return failure();
1293 }
1294 caseDestinations.push_back(destination);
1295 caseOperands.emplace_back(operands);
1296 caseOperandTypes.emplace_back(operandTypes);
1297 return success();
1298 };
1299 if (failed(parser.parseCommaSeparatedList(parseCase)))
1300 return failure();
1301
1302 caseValues = ArrayAttr::get(flagType.getContext(), values);
1303
1304 return parser.parseRSquare();
1305}
1306
1307static void printSwitchFlatOpCases(OpAsmPrinter &p, cir::SwitchFlatOp op,
1308 Type flagType, mlir::ArrayAttr caseValues,
1309 SuccessorRange caseDestinations,
1310 OperandRangeRange caseOperands,
1311 const TypeRangeRange &caseOperandTypes) {
1312 p << '[';
1313 p.printNewline();
1314 if (!caseValues) {
1315 p << ']';
1316 return;
1317 }
1318
1319 size_t index = 0;
1320 llvm::interleave(
1321 llvm::zip(caseValues, caseDestinations),
1322 [&](auto i) {
1323 p << " ";
1324 mlir::Attribute a = std::get<0>(i);
1325 p << mlir::cast<cir::IntAttr>(a).getValue();
1326 p << ": ";
1327 p.printSuccessorAndUseList(std::get<1>(i), caseOperands[index++]);
1328 },
1329 [&] {
1330 p << ',';
1331 p.printNewline();
1332 });
1333 p.printNewline();
1334 p << ']';
1335}
1336
1337//===----------------------------------------------------------------------===//
1338// GlobalOp
1339//===----------------------------------------------------------------------===//
1340
1341static ParseResult parseConstantValue(OpAsmParser &parser,
1342 mlir::Attribute &valueAttr) {
1343 NamedAttrList attr;
1344 return parser.parseAttribute(valueAttr, "value", attr);
1345}
1346
1347static void printConstant(OpAsmPrinter &p, Attribute value) {
1348 p.printAttribute(value);
1349}
1350
1351mlir::LogicalResult cir::GlobalOp::verify() {
1352 // Verify that the initial value, if present, is either a unit attribute or
1353 // an attribute CIR supports.
1354 if (getInitialValue().has_value()) {
1355 if (checkConstantTypes(getOperation(), getSymType(), *getInitialValue())
1356 .failed())
1357 return failure();
1358 }
1359
1360 // TODO(CIR): Many other checks for properties that haven't been upstreamed
1361 // yet.
1362
1363 return success();
1364}
1365
1366void cir::GlobalOp::build(
1367 OpBuilder &odsBuilder, OperationState &odsState, llvm::StringRef sym_name,
1368 mlir::Type sym_type, bool isConstant, cir::GlobalLinkageKind linkage,
1369 function_ref<void(OpBuilder &, Location)> ctorBuilder,
1370 function_ref<void(OpBuilder &, Location)> dtorBuilder) {
1371 odsState.addAttribute(getSymNameAttrName(odsState.name),
1372 odsBuilder.getStringAttr(sym_name));
1373 odsState.addAttribute(getSymTypeAttrName(odsState.name),
1374 mlir::TypeAttr::get(sym_type));
1375 if (isConstant)
1376 odsState.addAttribute(getConstantAttrName(odsState.name),
1377 odsBuilder.getUnitAttr());
1378
1379 cir::GlobalLinkageKindAttr linkageAttr =
1380 cir::GlobalLinkageKindAttr::get(odsBuilder.getContext(), linkage);
1381 odsState.addAttribute(getLinkageAttrName(odsState.name), linkageAttr);
1382
1383 Region *ctorRegion = odsState.addRegion();
1384 if (ctorBuilder) {
1385 odsBuilder.createBlock(ctorRegion);
1386 ctorBuilder(odsBuilder, odsState.location);
1387 }
1388
1389 Region *dtorRegion = odsState.addRegion();
1390 if (dtorBuilder) {
1391 odsBuilder.createBlock(dtorRegion);
1392 dtorBuilder(odsBuilder, odsState.location);
1393 }
1394
1395 odsState.addAttribute(getGlobalVisibilityAttrName(odsState.name),
1396 cir::VisibilityAttr::get(odsBuilder.getContext()));
1397}
1398
1399/// Given the region at `index`, or the parent operation if `index` is None,
1400/// return the successor regions. These are the regions that may be selected
1401/// during the flow of control. `operands` is a set of optional attributes that
1402/// correspond to a constant value for each operand, or null if that operand is
1403/// not a constant.
1404void cir::GlobalOp::getSuccessorRegions(
1405 mlir::RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &regions) {
1406 // The `ctor` and `dtor` regions always branch back to the parent operation.
1407 if (!point.isParent()) {
1408 regions.push_back(
1409 RegionSuccessor(getOperation(), getOperation()->getResults()));
1410 return;
1411 }
1412
1413 // Don't consider the ctor region if it is empty.
1414 Region *ctorRegion = &this->getCtorRegion();
1415 if (ctorRegion->empty())
1416 ctorRegion = nullptr;
1417
1418 // Don't consider the dtor region if it is empty.
1419 Region *dtorRegion = &this->getCtorRegion();
1420 if (dtorRegion->empty())
1421 dtorRegion = nullptr;
1422
1423 // If the condition isn't constant, both regions may be executed.
1424 if (ctorRegion)
1425 regions.push_back(RegionSuccessor(ctorRegion));
1426 if (dtorRegion)
1427 regions.push_back(RegionSuccessor(dtorRegion));
1428}
1429
1430static void printGlobalOpTypeAndInitialValue(OpAsmPrinter &p, cir::GlobalOp op,
1431 TypeAttr type, Attribute initAttr,
1432 mlir::Region &ctorRegion,
1433 mlir::Region &dtorRegion) {
1434 auto printType = [&]() { p << ": " << type; };
1435 if (!op.isDeclaration()) {
1436 p << "= ";
1437 if (!ctorRegion.empty()) {
1438 p << "ctor ";
1439 printType();
1440 p << " ";
1441 p.printRegion(ctorRegion,
1442 /*printEntryBlockArgs=*/false,
1443 /*printBlockTerminators=*/false);
1444 } else {
1445 // This also prints the type...
1446 if (initAttr)
1447 printConstant(p, initAttr);
1448 }
1449
1450 if (!dtorRegion.empty()) {
1451 p << " dtor ";
1452 p.printRegion(dtorRegion,
1453 /*printEntryBlockArgs=*/false,
1454 /*printBlockTerminators=*/false);
1455 }
1456 } else {
1457 printType();
1458 }
1459}
1460
1461static ParseResult parseGlobalOpTypeAndInitialValue(OpAsmParser &parser,
1462 TypeAttr &typeAttr,
1463 Attribute &initialValueAttr,
1464 mlir::Region &ctorRegion,
1465 mlir::Region &dtorRegion) {
1466 mlir::Type opTy;
1467 if (parser.parseOptionalEqual().failed()) {
1468 // Absence of equal means a declaration, so we need to parse the type.
1469 // cir.global @a : !cir.int<s, 32>
1470 if (parser.parseColonType(opTy))
1471 return failure();
1472 } else {
1473 // Parse contructor, example:
1474 // cir.global @rgb = ctor : type { ... }
1475 if (!parser.parseOptionalKeyword("ctor")) {
1476 if (parser.parseColonType(opTy))
1477 return failure();
1478 auto parseLoc = parser.getCurrentLocation();
1479 if (parser.parseRegion(ctorRegion, /*arguments=*/{}, /*argTypes=*/{}))
1480 return failure();
1481 if (ensureRegionTerm(parser, ctorRegion, parseLoc).failed())
1482 return failure();
1483 } else {
1484 // Parse constant with initializer, examples:
1485 // cir.global @y = 3.400000e+00 : f32
1486 // cir.global @rgb = #cir.const_array<[...] : !cir.array<i8 x 3>>
1487 if (parseConstantValue(parser, initialValueAttr).failed())
1488 return failure();
1489
1490 assert(mlir::isa<mlir::TypedAttr>(initialValueAttr) &&
1491 "Non-typed attrs shouldn't appear here.");
1492 auto typedAttr = mlir::cast<mlir::TypedAttr>(initialValueAttr);
1493 opTy = typedAttr.getType();
1494 }
1495
1496 // Parse destructor, example:
1497 // dtor { ... }
1498 if (!parser.parseOptionalKeyword("dtor")) {
1499 auto parseLoc = parser.getCurrentLocation();
1500 if (parser.parseRegion(dtorRegion, /*arguments=*/{}, /*argTypes=*/{}))
1501 return failure();
1502 if (ensureRegionTerm(parser, dtorRegion, parseLoc).failed())
1503 return failure();
1504 }
1505 }
1506
1507 typeAttr = TypeAttr::get(opTy);
1508 return success();
1509}
1510
1511//===----------------------------------------------------------------------===//
1512// GetGlobalOp
1513//===----------------------------------------------------------------------===//
1514
1515LogicalResult
1516cir::GetGlobalOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1517 // Verify that the result type underlying pointer type matches the type of
1518 // the referenced cir.global or cir.func op.
1519 mlir::Operation *op =
1520 symbolTable.lookupNearestSymbolFrom(*this, getNameAttr());
1521 if (op == nullptr || !(isa<GlobalOp>(op) || isa<FuncOp>(op)))
1522 return emitOpError("'")
1523 << getName()
1524 << "' does not reference a valid cir.global or cir.func";
1525
1526 mlir::Type symTy;
1527 if (auto g = dyn_cast<GlobalOp>(op)) {
1528 symTy = g.getSymType();
1531 } else if (auto f = dyn_cast<FuncOp>(op)) {
1532 symTy = f.getFunctionType();
1533 } else {
1534 llvm_unreachable("Unexpected operation for GetGlobalOp");
1535 }
1536
1537 auto resultType = dyn_cast<PointerType>(getAddr().getType());
1538 if (!resultType || symTy != resultType.getPointee())
1539 return emitOpError("result type pointee type '")
1540 << resultType.getPointee() << "' does not match type " << symTy
1541 << " of the global @" << getName();
1542
1543 return success();
1544}
1545
1546//===----------------------------------------------------------------------===//
1547// VTableAddrPointOp
1548//===----------------------------------------------------------------------===//
1549
1550LogicalResult
1551cir::VTableAddrPointOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1552 StringRef name = getName();
1553
1554 // Verify that the result type underlying pointer type matches the type of
1555 // the referenced cir.global.
1556 auto op =
1557 symbolTable.lookupNearestSymbolFrom<cir::GlobalOp>(*this, getNameAttr());
1558 if (!op)
1559 return emitOpError("'")
1560 << name << "' does not reference a valid cir.global";
1561 std::optional<mlir::Attribute> init = op.getInitialValue();
1562 if (!init)
1563 return success();
1564 if (!isa<cir::VTableAttr>(*init))
1565 return emitOpError("Expected #cir.vtable in initializer for global '")
1566 << name << "'";
1567 return success();
1568}
1569
1570//===----------------------------------------------------------------------===//
1571// VTTAddrPointOp
1572//===----------------------------------------------------------------------===//
1573
1574LogicalResult
1575cir::VTTAddrPointOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1576 // VTT ptr is not coming from a symbol.
1577 if (!getName())
1578 return success();
1579 StringRef name = *getName();
1580
1581 // Verify that the result type underlying pointer type matches the type of
1582 // the referenced cir.global op.
1583 auto op =
1584 symbolTable.lookupNearestSymbolFrom<cir::GlobalOp>(*this, getNameAttr());
1585 if (!op)
1586 return emitOpError("'")
1587 << name << "' does not reference a valid cir.global";
1588 std::optional<mlir::Attribute> init = op.getInitialValue();
1589 if (!init)
1590 return success();
1591 if (!isa<cir::ConstArrayAttr>(*init))
1592 return emitOpError(
1593 "Expected constant array in initializer for global VTT '")
1594 << name << "'";
1595 return success();
1596}
1597
1598LogicalResult cir::VTTAddrPointOp::verify() {
1599 // The operation uses either a symbol or a value to operate, but not both
1600 if (getName() && getSymAddr())
1601 return emitOpError("should use either a symbol or value, but not both");
1602
1603 // If not a symbol, stick with the concrete type used for getSymAddr.
1604 if (getSymAddr())
1605 return success();
1606
1607 mlir::Type resultType = getAddr().getType();
1608 mlir::Type resTy = cir::PointerType::get(
1609 cir::PointerType::get(cir::VoidType::get(getContext())));
1610
1611 if (resultType != resTy)
1612 return emitOpError("result type must be ")
1613 << resTy << ", but provided result type is " << resultType;
1614 return success();
1615}
1616
1617//===----------------------------------------------------------------------===//
1618// FuncOp
1619//===----------------------------------------------------------------------===//
1620
1621/// Returns the name used for the linkage attribute. This *must* correspond to
1622/// the name of the attribute in ODS.
1623static llvm::StringRef getLinkageAttrNameString() { return "linkage"; }
1624
1625void cir::FuncOp::build(OpBuilder &builder, OperationState &result,
1626 StringRef name, FuncType type,
1627 GlobalLinkageKind linkage) {
1628 result.addRegion();
1629 result.addAttribute(SymbolTable::getSymbolAttrName(),
1630 builder.getStringAttr(name));
1631 result.addAttribute(getFunctionTypeAttrName(result.name),
1632 TypeAttr::get(type));
1633 result.addAttribute(
1635 GlobalLinkageKindAttr::get(builder.getContext(), linkage));
1636 result.addAttribute(getGlobalVisibilityAttrName(result.name),
1637 cir::VisibilityAttr::get(builder.getContext()));
1638}
1639
1640ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) {
1641 llvm::SMLoc loc = parser.getCurrentLocation();
1642 mlir::Builder &builder = parser.getBuilder();
1643
1644 mlir::StringAttr builtinNameAttr = getBuiltinAttrName(state.name);
1645 mlir::StringAttr coroutineNameAttr = getCoroutineAttrName(state.name);
1646 mlir::StringAttr lambdaNameAttr = getLambdaAttrName(state.name);
1647 mlir::StringAttr noProtoNameAttr = getNoProtoAttrName(state.name);
1648 mlir::StringAttr visNameAttr = getSymVisibilityAttrName(state.name);
1649 mlir::StringAttr visibilityNameAttr = getGlobalVisibilityAttrName(state.name);
1650 mlir::StringAttr dsoLocalNameAttr = getDsoLocalAttrName(state.name);
1651
1652 if (::mlir::succeeded(parser.parseOptionalKeyword(builtinNameAttr.strref())))
1653 state.addAttribute(builtinNameAttr, parser.getBuilder().getUnitAttr());
1654 if (::mlir::succeeded(
1655 parser.parseOptionalKeyword(coroutineNameAttr.strref())))
1656 state.addAttribute(coroutineNameAttr, parser.getBuilder().getUnitAttr());
1657 if (::mlir::succeeded(parser.parseOptionalKeyword(lambdaNameAttr.strref())))
1658 state.addAttribute(lambdaNameAttr, parser.getBuilder().getUnitAttr());
1659 if (parser.parseOptionalKeyword(noProtoNameAttr).succeeded())
1660 state.addAttribute(noProtoNameAttr, parser.getBuilder().getUnitAttr());
1661
1662 // Default to external linkage if no keyword is provided.
1663 state.addAttribute(getLinkageAttrNameString(),
1664 GlobalLinkageKindAttr::get(
1665 parser.getContext(),
1667 parser, GlobalLinkageKind::ExternalLinkage)));
1668
1669 ::llvm::StringRef visAttrStr;
1670 if (parser.parseOptionalKeyword(&visAttrStr, {"private", "public", "nested"})
1671 .succeeded()) {
1672 state.addAttribute(visNameAttr,
1673 parser.getBuilder().getStringAttr(visAttrStr));
1674 }
1675
1676 cir::VisibilityAttr cirVisibilityAttr;
1677 parseVisibilityAttr(parser, cirVisibilityAttr);
1678 state.addAttribute(visibilityNameAttr, cirVisibilityAttr);
1679
1680 if (parser.parseOptionalKeyword(dsoLocalNameAttr).succeeded())
1681 state.addAttribute(dsoLocalNameAttr, parser.getBuilder().getUnitAttr());
1682
1683 StringAttr nameAttr;
1684 if (parser.parseSymbolName(nameAttr, SymbolTable::getSymbolAttrName(),
1685 state.attributes))
1686 return failure();
1690 bool isVariadic = false;
1691 if (function_interface_impl::parseFunctionSignatureWithArguments(
1692 parser, /*allowVariadic=*/true, arguments, isVariadic, resultTypes,
1693 resultAttrs))
1694 return failure();
1696 for (OpAsmParser::Argument &arg : arguments)
1697 argTypes.push_back(arg.type);
1698
1699 if (resultTypes.size() > 1) {
1700 return parser.emitError(
1701 loc, "functions with multiple return types are not supported");
1702 }
1703
1704 mlir::Type returnType =
1705 (resultTypes.empty() ? cir::VoidType::get(builder.getContext())
1706 : resultTypes.front());
1707
1708 cir::FuncType fnType = cir::FuncType::get(argTypes, returnType, isVariadic);
1709 if (!fnType)
1710 return failure();
1711 state.addAttribute(getFunctionTypeAttrName(state.name),
1712 TypeAttr::get(fnType));
1713
1714 bool hasAlias = false;
1715 mlir::StringAttr aliaseeNameAttr = getAliaseeAttrName(state.name);
1716 if (parser.parseOptionalKeyword("alias").succeeded()) {
1717 if (parser.parseLParen().failed())
1718 return failure();
1719 mlir::StringAttr aliaseeAttr;
1720 if (parser.parseOptionalSymbolName(aliaseeAttr).failed())
1721 return failure();
1722 state.addAttribute(aliaseeNameAttr, FlatSymbolRefAttr::get(aliaseeAttr));
1723 if (parser.parseRParen().failed())
1724 return failure();
1725 hasAlias = true;
1726 }
1727
1728 auto parseGlobalDtorCtor =
1729 [&](StringRef keyword,
1730 llvm::function_ref<void(std::optional<int> prio)> createAttr)
1731 -> mlir::LogicalResult {
1732 if (mlir::succeeded(parser.parseOptionalKeyword(keyword))) {
1733 std::optional<int> priority;
1734 if (mlir::succeeded(parser.parseOptionalLParen())) {
1735 auto parsedPriority = mlir::FieldParser<int>::parse(parser);
1736 if (mlir::failed(parsedPriority))
1737 return parser.emitError(parser.getCurrentLocation(),
1738 "failed to parse 'priority', of type 'int'");
1739 priority = parsedPriority.value_or(int());
1740 // Parse literal ')'
1741 if (parser.parseRParen())
1742 return failure();
1743 }
1744 createAttr(priority);
1745 }
1746 return success();
1747 };
1748
1749 if (parseGlobalDtorCtor("global_ctor", [&](std::optional<int> priority) {
1750 mlir::IntegerAttr globalCtorPriorityAttr =
1751 builder.getI32IntegerAttr(priority.value_or(65535));
1752 state.addAttribute(getGlobalCtorPriorityAttrName(state.name),
1753 globalCtorPriorityAttr);
1754 }).failed())
1755 return failure();
1756
1757 if (parseGlobalDtorCtor("global_dtor", [&](std::optional<int> priority) {
1758 mlir::IntegerAttr globalDtorPriorityAttr =
1759 builder.getI32IntegerAttr(priority.value_or(65535));
1760 state.addAttribute(getGlobalDtorPriorityAttrName(state.name),
1761 globalDtorPriorityAttr);
1762 }).failed())
1763 return failure();
1764
1765 // Parse optional inline kind: inline(never|always|hint)
1766 if (parser.parseOptionalKeyword("inline").succeeded()) {
1767 if (parser.parseLParen().failed())
1768 return failure();
1769
1770 llvm::StringRef inlineKindStr;
1771 const std::array<llvm::StringRef, cir::getMaxEnumValForInlineKind()>
1772 allowedInlineKindStrs{
1773 cir::stringifyInlineKind(cir::InlineKind::NoInline),
1774 cir::stringifyInlineKind(cir::InlineKind::AlwaysInline),
1775 cir::stringifyInlineKind(cir::InlineKind::InlineHint),
1776 };
1777 if (parser.parseOptionalKeyword(&inlineKindStr, allowedInlineKindStrs)
1778 .failed())
1779 return parser.emitError(parser.getCurrentLocation(),
1780 "expected 'never', 'always', or 'hint'");
1781
1782 std::optional<InlineKind> inlineKind =
1783 cir::symbolizeInlineKind(inlineKindStr);
1784 if (!inlineKind)
1785 return parser.emitError(parser.getCurrentLocation(),
1786 "invalid inline kind");
1787
1788 state.addAttribute(getInlineKindAttrName(state.name),
1789 cir::InlineAttr::get(builder.getContext(), *inlineKind));
1790
1791 if (parser.parseRParen().failed())
1792 return failure();
1793 }
1794
1795 // Parse the optional function body.
1796 auto *body = state.addRegion();
1797 OptionalParseResult parseResult = parser.parseOptionalRegion(
1798 *body, arguments, /*enableNameShadowing=*/false);
1799 if (parseResult.has_value()) {
1800 if (hasAlias)
1801 return parser.emitError(loc, "function alias shall not have a body");
1802 if (failed(*parseResult))
1803 return failure();
1804 // Function body was parsed, make sure its not empty.
1805 if (body->empty())
1806 return parser.emitError(loc, "expected non-empty function body");
1807 }
1808
1809 return success();
1810}
1811
1812// This function corresponds to `llvm::GlobalValue::isDeclaration` and should
1813// have a similar implementation. We don't currently ifuncs or materializable
1814// functions, but those should be handled here as they are implemented.
1815bool cir::FuncOp::isDeclaration() {
1817
1818 std::optional<StringRef> aliasee = getAliasee();
1819 if (!aliasee)
1820 return getFunctionBody().empty();
1821
1822 // Aliases are always definitions.
1823 return false;
1824}
1825
1826mlir::Region *cir::FuncOp::getCallableRegion() {
1827 // TODO(CIR): This function will have special handling for aliases and a
1828 // check for an external function, once those features have been upstreamed.
1829 return &getBody();
1830}
1831
1832void cir::FuncOp::print(OpAsmPrinter &p) {
1833 if (getBuiltin())
1834 p << " builtin";
1835
1836 if (getCoroutine())
1837 p << " coroutine";
1838
1839 if (getLambda())
1840 p << " lambda";
1841
1842 if (getNoProto())
1843 p << " no_proto";
1844
1845 if (getComdat())
1846 p << " comdat";
1847
1848 if (getLinkage() != GlobalLinkageKind::ExternalLinkage)
1849 p << ' ' << stringifyGlobalLinkageKind(getLinkage());
1850
1851 mlir::SymbolTable::Visibility vis = getVisibility();
1852 if (vis != mlir::SymbolTable::Visibility::Public)
1853 p << ' ' << vis;
1854
1855 cir::VisibilityAttr cirVisibilityAttr = getGlobalVisibilityAttr();
1856 if (!cirVisibilityAttr.isDefault()) {
1857 p << ' ';
1858 printVisibilityAttr(p, cirVisibilityAttr);
1859 }
1860
1861 if (getDsoLocal())
1862 p << " dso_local";
1863
1864 p << ' ';
1865 p.printSymbolName(getSymName());
1866 cir::FuncType fnType = getFunctionType();
1867 function_interface_impl::printFunctionSignature(
1868 p, *this, fnType.getInputs(), fnType.isVarArg(), fnType.getReturnTypes());
1869
1870 if (std::optional<StringRef> aliaseeName = getAliasee()) {
1871 p << " alias(";
1872 p.printSymbolName(*aliaseeName);
1873 p << ")";
1874 }
1875
1876 if (auto globalCtorPriority = getGlobalCtorPriority()) {
1877 p << " global_ctor";
1878 if (globalCtorPriority.value() != 65535)
1879 p << "(" << globalCtorPriority.value() << ")";
1880 }
1881
1882 if (auto globalDtorPriority = getGlobalDtorPriority()) {
1883 p << " global_dtor";
1884 if (globalDtorPriority.value() != 65535)
1885 p << "(" << globalDtorPriority.value() << ")";
1886 }
1887
1888 if (cir::InlineAttr inlineAttr = getInlineKindAttr()) {
1889 p << " inline(" << cir::stringifyInlineKind(inlineAttr.getValue()) << ")";
1890 }
1891
1892 // Print the body if this is not an external function.
1893 Region &body = getOperation()->getRegion(0);
1894 if (!body.empty()) {
1895 p << ' ';
1896 p.printRegion(body, /*printEntryBlockArgs=*/false,
1897 /*printBlockTerminators=*/true);
1898 }
1899}
1900
1901mlir::LogicalResult cir::FuncOp::verify() {
1902
1903 llvm::SmallSet<llvm::StringRef, 16> labels;
1904 llvm::SmallSet<llvm::StringRef, 16> gotos;
1905
1906 getOperation()->walk([&](mlir::Operation *op) {
1907 if (auto lab = dyn_cast<cir::LabelOp>(op)) {
1908 labels.insert(lab.getLabel());
1909 } else if (auto goTo = dyn_cast<cir::GotoOp>(op)) {
1910 gotos.insert(goTo.getLabel());
1911 }
1912 });
1913
1914 if (!labels.empty() || !gotos.empty()) {
1915 llvm::SmallSet<llvm::StringRef, 16> mismatched =
1916 llvm::set_difference(gotos, labels);
1917
1918 if (!mismatched.empty())
1919 return emitOpError() << "goto/label mismatch";
1920 }
1921 return success();
1922}
1923
1924//===----------------------------------------------------------------------===//
1925// BinOp
1926//===----------------------------------------------------------------------===//
1927LogicalResult cir::BinOp::verify() {
1928 bool noWrap = getNoUnsignedWrap() || getNoSignedWrap();
1929 bool saturated = getSaturated();
1930
1931 if (!isa<cir::IntType>(getType()) && noWrap)
1932 return emitError()
1933 << "only operations on integer values may have nsw/nuw flags";
1934
1935 bool noWrapOps = getKind() == cir::BinOpKind::Add ||
1936 getKind() == cir::BinOpKind::Sub ||
1937 getKind() == cir::BinOpKind::Mul;
1938
1939 bool saturatedOps =
1940 getKind() == cir::BinOpKind::Add || getKind() == cir::BinOpKind::Sub;
1941
1942 if (noWrap && !noWrapOps)
1943 return emitError() << "The nsw/nuw flags are applicable to opcodes: 'add', "
1944 "'sub' and 'mul'";
1945 if (saturated && !saturatedOps)
1946 return emitError() << "The saturated flag is applicable to opcodes: 'add' "
1947 "and 'sub'";
1948 if (noWrap && saturated)
1949 return emitError() << "The nsw/nuw flags and the saturated flag are "
1950 "mutually exclusive";
1951
1952 return mlir::success();
1953}
1954
1955//===----------------------------------------------------------------------===//
1956// TernaryOp
1957//===----------------------------------------------------------------------===//
1958
1959/// Given the region at `point`, or the parent operation if `point` is None,
1960/// return the successor regions. These are the regions that may be selected
1961/// during the flow of control. `operands` is a set of optional attributes that
1962/// correspond to a constant value for each operand, or null if that operand is
1963/// not a constant.
1964void cir::TernaryOp::getSuccessorRegions(
1965 mlir::RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &regions) {
1966 // The `true` and the `false` region branch back to the parent operation.
1967 if (!point.isParent()) {
1968 regions.push_back(RegionSuccessor(getOperation(), this->getODSResults(0)));
1969 return;
1970 }
1971
1972 // When branching from the parent operation, both the true and false
1973 // regions are considered possible successors
1974 regions.push_back(RegionSuccessor(&getTrueRegion()));
1975 regions.push_back(RegionSuccessor(&getFalseRegion()));
1976}
1977
1978void cir::TernaryOp::build(
1979 OpBuilder &builder, OperationState &result, Value cond,
1980 function_ref<void(OpBuilder &, Location)> trueBuilder,
1981 function_ref<void(OpBuilder &, Location)> falseBuilder) {
1982 result.addOperands(cond);
1983 OpBuilder::InsertionGuard guard(builder);
1984 Region *trueRegion = result.addRegion();
1985 builder.createBlock(trueRegion);
1986 trueBuilder(builder, result.location);
1987 Region *falseRegion = result.addRegion();
1988 builder.createBlock(falseRegion);
1989 falseBuilder(builder, result.location);
1990
1991 // Get result type from whichever branch has a yield (the other may have
1992 // unreachable from a throw expression)
1993 auto yield =
1994 dyn_cast_or_null<cir::YieldOp>(trueRegion->back().getTerminator());
1995 if (!yield)
1996 yield = dyn_cast_or_null<cir::YieldOp>(falseRegion->back().getTerminator());
1997
1998 assert((yield && yield.getNumOperands() <= 1) &&
1999 "expected zero or one result type");
2000 if (yield.getNumOperands() == 1)
2001 result.addTypes(TypeRange{yield.getOperandTypes().front()});
2002}
2003
2004//===----------------------------------------------------------------------===//
2005// SelectOp
2006//===----------------------------------------------------------------------===//
2007
2008OpFoldResult cir::SelectOp::fold(FoldAdaptor adaptor) {
2009 mlir::Attribute condition = adaptor.getCondition();
2010 if (condition) {
2011 bool conditionValue = mlir::cast<cir::BoolAttr>(condition).getValue();
2012 return conditionValue ? getTrueValue() : getFalseValue();
2013 }
2014
2015 // cir.select if %0 then x else x -> x
2016 mlir::Attribute trueValue = adaptor.getTrueValue();
2017 mlir::Attribute falseValue = adaptor.getFalseValue();
2018 if (trueValue == falseValue)
2019 return trueValue;
2020 if (getTrueValue() == getFalseValue())
2021 return getTrueValue();
2022
2023 return {};
2024}
2025
2026//===----------------------------------------------------------------------===//
2027// ShiftOp
2028//===----------------------------------------------------------------------===//
2029LogicalResult cir::ShiftOp::verify() {
2030 mlir::Operation *op = getOperation();
2031 auto op0VecTy = mlir::dyn_cast<cir::VectorType>(op->getOperand(0).getType());
2032 auto op1VecTy = mlir::dyn_cast<cir::VectorType>(op->getOperand(1).getType());
2033 if (!op0VecTy ^ !op1VecTy)
2034 return emitOpError() << "input types cannot be one vector and one scalar";
2035
2036 if (op0VecTy) {
2037 if (op0VecTy.getSize() != op1VecTy.getSize())
2038 return emitOpError() << "input vector types must have the same size";
2039
2040 auto opResultTy = mlir::dyn_cast<cir::VectorType>(getType());
2041 if (!opResultTy)
2042 return emitOpError() << "the type of the result must be a vector "
2043 << "if it is vector shift";
2044
2045 auto op0VecEleTy = mlir::cast<cir::IntType>(op0VecTy.getElementType());
2046 auto op1VecEleTy = mlir::cast<cir::IntType>(op1VecTy.getElementType());
2047 if (op0VecEleTy.getWidth() != op1VecEleTy.getWidth())
2048 return emitOpError()
2049 << "vector operands do not have the same elements sizes";
2050
2051 auto resVecEleTy = mlir::cast<cir::IntType>(opResultTy.getElementType());
2052 if (op0VecEleTy.getWidth() != resVecEleTy.getWidth())
2053 return emitOpError() << "vector operands and result type do not have the "
2054 "same elements sizes";
2055 }
2056
2057 return mlir::success();
2058}
2059
2060//===----------------------------------------------------------------------===//
2061// LabelOp Definitions
2062//===----------------------------------------------------------------------===//
2063
2064LogicalResult cir::LabelOp::verify() {
2065 mlir::Operation *op = getOperation();
2066 mlir::Block *blk = op->getBlock();
2067 if (&blk->front() != op)
2068 return emitError() << "must be the first operation in a block";
2069
2070 return mlir::success();
2071}
2072
2073//===----------------------------------------------------------------------===//
2074// UnaryOp
2075//===----------------------------------------------------------------------===//
2076
2077LogicalResult cir::UnaryOp::verify() {
2078 switch (getKind()) {
2079 case cir::UnaryOpKind::Inc:
2080 case cir::UnaryOpKind::Dec:
2081 case cir::UnaryOpKind::Plus:
2082 case cir::UnaryOpKind::Minus:
2083 case cir::UnaryOpKind::Not:
2084 // Nothing to verify.
2085 return success();
2086 }
2087
2088 llvm_unreachable("Unknown UnaryOp kind?");
2089}
2090
2091static bool isBoolNot(cir::UnaryOp op) {
2092 return isa<cir::BoolType>(op.getInput().getType()) &&
2093 op.getKind() == cir::UnaryOpKind::Not;
2094}
2095
2096// This folder simplifies the sequential boolean not operations.
2097// For instance, the next two unary operations will be eliminated:
2098//
2099// ```mlir
2100// %1 = cir.unary(not, %0) : !cir.bool, !cir.bool
2101// %2 = cir.unary(not, %1) : !cir.bool, !cir.bool
2102// ```
2103//
2104// and the argument of the first one (%0) will be used instead.
2105OpFoldResult cir::UnaryOp::fold(FoldAdaptor adaptor) {
2106 if (auto poison =
2107 mlir::dyn_cast_if_present<cir::PoisonAttr>(adaptor.getInput())) {
2108 // Propagate poison values
2109 return poison;
2110 }
2111
2112 if (isBoolNot(*this))
2113 if (auto previous = getInput().getDefiningOp<cir::UnaryOp>())
2114 if (isBoolNot(previous))
2115 return previous.getInput();
2116
2117 return {};
2118}
2119
2120//===----------------------------------------------------------------------===//
2121// CopyOp Definitions
2122//===----------------------------------------------------------------------===//
2123
2124LogicalResult cir::CopyOp::verify() {
2125 // A data layout is required for us to know the number of bytes to be copied.
2126 if (!getType().getPointee().hasTrait<DataLayoutTypeInterface::Trait>())
2127 return emitError() << "missing data layout for pointee type";
2128
2129 if (getSrc() == getDst())
2130 return emitError() << "source and destination are the same";
2131
2132 return mlir::success();
2133}
2134
2135//===----------------------------------------------------------------------===//
2136// GetMemberOp Definitions
2137//===----------------------------------------------------------------------===//
2138
2139LogicalResult cir::GetMemberOp::verify() {
2140 const auto recordTy = dyn_cast<RecordType>(getAddrTy().getPointee());
2141 if (!recordTy)
2142 return emitError() << "expected pointer to a record type";
2143
2144 if (recordTy.getMembers().size() <= getIndex())
2145 return emitError() << "member index out of bounds";
2146
2147 if (recordTy.getMembers()[getIndex()] != getType().getPointee())
2148 return emitError() << "member type mismatch";
2149
2150 return mlir::success();
2151}
2152
2153//===----------------------------------------------------------------------===//
2154// VecCreateOp
2155//===----------------------------------------------------------------------===//
2156
2157OpFoldResult cir::VecCreateOp::fold(FoldAdaptor adaptor) {
2158 if (llvm::any_of(getElements(), [](mlir::Value value) {
2159 return !value.getDefiningOp<cir::ConstantOp>();
2160 }))
2161 return {};
2162
2163 return cir::ConstVectorAttr::get(
2164 getType(), mlir::ArrayAttr::get(getContext(), adaptor.getElements()));
2165}
2166
2167LogicalResult cir::VecCreateOp::verify() {
2168 // Verify that the number of arguments matches the number of elements in the
2169 // vector, and that the type of all the arguments matches the type of the
2170 // elements in the vector.
2171 const cir::VectorType vecTy = getType();
2172 if (getElements().size() != vecTy.getSize()) {
2173 return emitOpError() << "operand count of " << getElements().size()
2174 << " doesn't match vector type " << vecTy
2175 << " element count of " << vecTy.getSize();
2176 }
2177
2178 const mlir::Type elementType = vecTy.getElementType();
2179 for (const mlir::Value element : getElements()) {
2180 if (element.getType() != elementType) {
2181 return emitOpError() << "operand type " << element.getType()
2182 << " doesn't match vector element type "
2183 << elementType;
2184 }
2185 }
2186
2187 return success();
2188}
2189
2190//===----------------------------------------------------------------------===//
2191// VecExtractOp
2192//===----------------------------------------------------------------------===//
2193
2194OpFoldResult cir::VecExtractOp::fold(FoldAdaptor adaptor) {
2195 const auto vectorAttr =
2196 llvm::dyn_cast_if_present<cir::ConstVectorAttr>(adaptor.getVec());
2197 if (!vectorAttr)
2198 return {};
2199
2200 const auto indexAttr =
2201 llvm::dyn_cast_if_present<cir::IntAttr>(adaptor.getIndex());
2202 if (!indexAttr)
2203 return {};
2204
2205 const mlir::ArrayAttr elements = vectorAttr.getElts();
2206 const uint64_t index = indexAttr.getUInt();
2207 if (index >= elements.size())
2208 return {};
2209
2210 return elements[index];
2211}
2212
2213//===----------------------------------------------------------------------===//
2214// VecCmpOp
2215//===----------------------------------------------------------------------===//
2216
2217OpFoldResult cir::VecCmpOp::fold(FoldAdaptor adaptor) {
2218 auto lhsVecAttr =
2219 mlir::dyn_cast_if_present<cir::ConstVectorAttr>(adaptor.getLhs());
2220 auto rhsVecAttr =
2221 mlir::dyn_cast_if_present<cir::ConstVectorAttr>(adaptor.getRhs());
2222 if (!lhsVecAttr || !rhsVecAttr)
2223 return {};
2224
2225 mlir::Type inputElemTy =
2226 mlir::cast<cir::VectorType>(lhsVecAttr.getType()).getElementType();
2227 if (!isAnyIntegerOrFloatingPointType(inputElemTy))
2228 return {};
2229
2230 cir::CmpOpKind opKind = adaptor.getKind();
2231 mlir::ArrayAttr lhsVecElhs = lhsVecAttr.getElts();
2232 mlir::ArrayAttr rhsVecElhs = rhsVecAttr.getElts();
2233 uint64_t vecSize = lhsVecElhs.size();
2234
2235 SmallVector<mlir::Attribute, 16> elements(vecSize);
2236 bool isIntAttr = vecSize && mlir::isa<cir::IntAttr>(lhsVecElhs[0]);
2237 for (uint64_t i = 0; i < vecSize; i++) {
2238 mlir::Attribute lhsAttr = lhsVecElhs[i];
2239 mlir::Attribute rhsAttr = rhsVecElhs[i];
2240 int cmpResult = 0;
2241 switch (opKind) {
2242 case cir::CmpOpKind::lt: {
2243 if (isIntAttr) {
2244 cmpResult = mlir::cast<cir::IntAttr>(lhsAttr).getSInt() <
2245 mlir::cast<cir::IntAttr>(rhsAttr).getSInt();
2246 } else {
2247 cmpResult = mlir::cast<cir::FPAttr>(lhsAttr).getValue() <
2248 mlir::cast<cir::FPAttr>(rhsAttr).getValue();
2249 }
2250 break;
2251 }
2252 case cir::CmpOpKind::le: {
2253 if (isIntAttr) {
2254 cmpResult = mlir::cast<cir::IntAttr>(lhsAttr).getSInt() <=
2255 mlir::cast<cir::IntAttr>(rhsAttr).getSInt();
2256 } else {
2257 cmpResult = mlir::cast<cir::FPAttr>(lhsAttr).getValue() <=
2258 mlir::cast<cir::FPAttr>(rhsAttr).getValue();
2259 }
2260 break;
2261 }
2262 case cir::CmpOpKind::gt: {
2263 if (isIntAttr) {
2264 cmpResult = mlir::cast<cir::IntAttr>(lhsAttr).getSInt() >
2265 mlir::cast<cir::IntAttr>(rhsAttr).getSInt();
2266 } else {
2267 cmpResult = mlir::cast<cir::FPAttr>(lhsAttr).getValue() >
2268 mlir::cast<cir::FPAttr>(rhsAttr).getValue();
2269 }
2270 break;
2271 }
2272 case cir::CmpOpKind::ge: {
2273 if (isIntAttr) {
2274 cmpResult = mlir::cast<cir::IntAttr>(lhsAttr).getSInt() >=
2275 mlir::cast<cir::IntAttr>(rhsAttr).getSInt();
2276 } else {
2277 cmpResult = mlir::cast<cir::FPAttr>(lhsAttr).getValue() >=
2278 mlir::cast<cir::FPAttr>(rhsAttr).getValue();
2279 }
2280 break;
2281 }
2282 case cir::CmpOpKind::eq: {
2283 if (isIntAttr) {
2284 cmpResult = mlir::cast<cir::IntAttr>(lhsAttr).getSInt() ==
2285 mlir::cast<cir::IntAttr>(rhsAttr).getSInt();
2286 } else {
2287 cmpResult = mlir::cast<cir::FPAttr>(lhsAttr).getValue() ==
2288 mlir::cast<cir::FPAttr>(rhsAttr).getValue();
2289 }
2290 break;
2291 }
2292 case cir::CmpOpKind::ne: {
2293 if (isIntAttr) {
2294 cmpResult = mlir::cast<cir::IntAttr>(lhsAttr).getSInt() !=
2295 mlir::cast<cir::IntAttr>(rhsAttr).getSInt();
2296 } else {
2297 cmpResult = mlir::cast<cir::FPAttr>(lhsAttr).getValue() !=
2298 mlir::cast<cir::FPAttr>(rhsAttr).getValue();
2299 }
2300 break;
2301 }
2302 }
2303
2304 elements[i] = cir::IntAttr::get(getType().getElementType(), cmpResult);
2305 }
2306
2307 return cir::ConstVectorAttr::get(
2308 getType(), mlir::ArrayAttr::get(getContext(), elements));
2309}
2310
2311//===----------------------------------------------------------------------===//
2312// VecShuffleOp
2313//===----------------------------------------------------------------------===//
2314
2315OpFoldResult cir::VecShuffleOp::fold(FoldAdaptor adaptor) {
2316 auto vec1Attr =
2317 mlir::dyn_cast_if_present<cir::ConstVectorAttr>(adaptor.getVec1());
2318 auto vec2Attr =
2319 mlir::dyn_cast_if_present<cir::ConstVectorAttr>(adaptor.getVec2());
2320 if (!vec1Attr || !vec2Attr)
2321 return {};
2322
2323 mlir::Type vec1ElemTy =
2324 mlir::cast<cir::VectorType>(vec1Attr.getType()).getElementType();
2325
2326 mlir::ArrayAttr vec1Elts = vec1Attr.getElts();
2327 mlir::ArrayAttr vec2Elts = vec2Attr.getElts();
2328 mlir::ArrayAttr indicesElts = adaptor.getIndices();
2329
2331 elements.reserve(indicesElts.size());
2332
2333 uint64_t vec1Size = vec1Elts.size();
2334 for (const auto &idxAttr : indicesElts.getAsRange<cir::IntAttr>()) {
2335 if (idxAttr.getSInt() == -1) {
2336 elements.push_back(cir::UndefAttr::get(vec1ElemTy));
2337 continue;
2338 }
2339
2340 uint64_t idxValue = idxAttr.getUInt();
2341 elements.push_back(idxValue < vec1Size ? vec1Elts[idxValue]
2342 : vec2Elts[idxValue - vec1Size]);
2343 }
2344
2345 return cir::ConstVectorAttr::get(
2346 getType(), mlir::ArrayAttr::get(getContext(), elements));
2347}
2348
2349LogicalResult cir::VecShuffleOp::verify() {
2350 // The number of elements in the indices array must match the number of
2351 // elements in the result type.
2352 if (getIndices().size() != getResult().getType().getSize()) {
2353 return emitOpError() << ": the number of elements in " << getIndices()
2354 << " and " << getResult().getType() << " don't match";
2355 }
2356
2357 // The element types of the two input vectors and of the result type must
2358 // match.
2359 if (getVec1().getType().getElementType() !=
2360 getResult().getType().getElementType()) {
2361 return emitOpError() << ": element types of " << getVec1().getType()
2362 << " and " << getResult().getType() << " don't match";
2363 }
2364
2365 const uint64_t maxValidIndex =
2366 getVec1().getType().getSize() + getVec2().getType().getSize() - 1;
2367 if (llvm::any_of(
2368 getIndices().getAsRange<cir::IntAttr>(), [&](cir::IntAttr idxAttr) {
2369 return idxAttr.getSInt() != -1 && idxAttr.getUInt() > maxValidIndex;
2370 })) {
2371 return emitOpError() << ": index for __builtin_shufflevector must be "
2372 "less than the total number of vector elements";
2373 }
2374 return success();
2375}
2376
2377//===----------------------------------------------------------------------===//
2378// VecShuffleDynamicOp
2379//===----------------------------------------------------------------------===//
2380
2381OpFoldResult cir::VecShuffleDynamicOp::fold(FoldAdaptor adaptor) {
2382 mlir::Attribute vec = adaptor.getVec();
2383 mlir::Attribute indices = adaptor.getIndices();
2384 if (mlir::isa_and_nonnull<cir::ConstVectorAttr>(vec) &&
2385 mlir::isa_and_nonnull<cir::ConstVectorAttr>(indices)) {
2386 auto vecAttr = mlir::cast<cir::ConstVectorAttr>(vec);
2387 auto indicesAttr = mlir::cast<cir::ConstVectorAttr>(indices);
2388
2389 mlir::ArrayAttr vecElts = vecAttr.getElts();
2390 mlir::ArrayAttr indicesElts = indicesAttr.getElts();
2391
2392 const uint64_t numElements = vecElts.size();
2393
2395 elements.reserve(numElements);
2396
2397 const uint64_t maskBits = llvm::NextPowerOf2(numElements - 1) - 1;
2398 for (const auto &idxAttr : indicesElts.getAsRange<cir::IntAttr>()) {
2399 uint64_t idxValue = idxAttr.getUInt();
2400 uint64_t newIdx = idxValue & maskBits;
2401 elements.push_back(vecElts[newIdx]);
2402 }
2403
2404 return cir::ConstVectorAttr::get(
2405 getType(), mlir::ArrayAttr::get(getContext(), elements));
2406 }
2407
2408 return {};
2409}
2410
2411LogicalResult cir::VecShuffleDynamicOp::verify() {
2412 // The number of elements in the two input vectors must match.
2413 if (getVec().getType().getSize() !=
2414 mlir::cast<cir::VectorType>(getIndices().getType()).getSize()) {
2415 return emitOpError() << ": the number of elements in " << getVec().getType()
2416 << " and " << getIndices().getType() << " don't match";
2417 }
2418 return success();
2419}
2420
2421//===----------------------------------------------------------------------===//
2422// VecTernaryOp
2423//===----------------------------------------------------------------------===//
2424
2425LogicalResult cir::VecTernaryOp::verify() {
2426 // Verify that the condition operand has the same number of elements as the
2427 // other operands. (The automatic verification already checked that all
2428 // operands are vector types and that the second and third operands are the
2429 // same type.)
2430 if (getCond().getType().getSize() != getLhs().getType().getSize()) {
2431 return emitOpError() << ": the number of elements in "
2432 << getCond().getType() << " and " << getLhs().getType()
2433 << " don't match";
2434 }
2435 return success();
2436}
2437
2438OpFoldResult cir::VecTernaryOp::fold(FoldAdaptor adaptor) {
2439 mlir::Attribute cond = adaptor.getCond();
2440 mlir::Attribute lhs = adaptor.getLhs();
2441 mlir::Attribute rhs = adaptor.getRhs();
2442
2443 if (!mlir::isa_and_nonnull<cir::ConstVectorAttr>(cond) ||
2444 !mlir::isa_and_nonnull<cir::ConstVectorAttr>(lhs) ||
2445 !mlir::isa_and_nonnull<cir::ConstVectorAttr>(rhs))
2446 return {};
2447 auto condVec = mlir::cast<cir::ConstVectorAttr>(cond);
2448 auto lhsVec = mlir::cast<cir::ConstVectorAttr>(lhs);
2449 auto rhsVec = mlir::cast<cir::ConstVectorAttr>(rhs);
2450
2451 mlir::ArrayAttr condElts = condVec.getElts();
2452
2454 elements.reserve(condElts.size());
2455
2456 for (const auto &[idx, condAttr] :
2457 llvm::enumerate(condElts.getAsRange<cir::IntAttr>())) {
2458 if (condAttr.getSInt()) {
2459 elements.push_back(lhsVec.getElts()[idx]);
2460 } else {
2461 elements.push_back(rhsVec.getElts()[idx]);
2462 }
2463 }
2464
2465 cir::VectorType vecTy = getLhs().getType();
2466 return cir::ConstVectorAttr::get(
2467 vecTy, mlir::ArrayAttr::get(getContext(), elements));
2468}
2469
2470//===----------------------------------------------------------------------===//
2471// ComplexCreateOp
2472//===----------------------------------------------------------------------===//
2473
2474LogicalResult cir::ComplexCreateOp::verify() {
2475 if (getType().getElementType() != getReal().getType()) {
2476 emitOpError()
2477 << "operand type of cir.complex.create does not match its result type";
2478 return failure();
2479 }
2480
2481 return success();
2482}
2483
2484OpFoldResult cir::ComplexCreateOp::fold(FoldAdaptor adaptor) {
2485 mlir::Attribute real = adaptor.getReal();
2486 mlir::Attribute imag = adaptor.getImag();
2487 if (!real || !imag)
2488 return {};
2489
2490 // When both of real and imag are constants, we can fold the operation into an
2491 // `#cir.const_complex` operation.
2492 auto realAttr = mlir::cast<mlir::TypedAttr>(real);
2493 auto imagAttr = mlir::cast<mlir::TypedAttr>(imag);
2494 return cir::ConstComplexAttr::get(realAttr, imagAttr);
2495}
2496
2497//===----------------------------------------------------------------------===//
2498// ComplexRealOp
2499//===----------------------------------------------------------------------===//
2500
2501LogicalResult cir::ComplexRealOp::verify() {
2502 mlir::Type operandTy = getOperand().getType();
2503 if (auto complexOperandTy = mlir::dyn_cast<cir::ComplexType>(operandTy))
2504 operandTy = complexOperandTy.getElementType();
2505
2506 if (getType() != operandTy) {
2507 emitOpError() << ": result type does not match operand type";
2508 return failure();
2509 }
2510
2511 return success();
2512}
2513
2514OpFoldResult cir::ComplexRealOp::fold(FoldAdaptor adaptor) {
2515 if (!mlir::isa<cir::ComplexType>(getOperand().getType()))
2516 return nullptr;
2517
2518 if (auto complexCreateOp = getOperand().getDefiningOp<cir::ComplexCreateOp>())
2519 return complexCreateOp.getOperand(0);
2520
2521 auto complex =
2522 mlir::cast_if_present<cir::ConstComplexAttr>(adaptor.getOperand());
2523 return complex ? complex.getReal() : nullptr;
2524}
2525
2526//===----------------------------------------------------------------------===//
2527// ComplexImagOp
2528//===----------------------------------------------------------------------===//
2529
2530LogicalResult cir::ComplexImagOp::verify() {
2531 mlir::Type operandTy = getOperand().getType();
2532 if (auto complexOperandTy = mlir::dyn_cast<cir::ComplexType>(operandTy))
2533 operandTy = complexOperandTy.getElementType();
2534
2535 if (getType() != operandTy) {
2536 emitOpError() << ": result type does not match operand type";
2537 return failure();
2538 }
2539
2540 return success();
2541}
2542
2543OpFoldResult cir::ComplexImagOp::fold(FoldAdaptor adaptor) {
2544 if (!mlir::isa<cir::ComplexType>(getOperand().getType()))
2545 return nullptr;
2546
2547 if (auto complexCreateOp = getOperand().getDefiningOp<cir::ComplexCreateOp>())
2548 return complexCreateOp.getOperand(1);
2549
2550 auto complex =
2551 mlir::cast_if_present<cir::ConstComplexAttr>(adaptor.getOperand());
2552 return complex ? complex.getImag() : nullptr;
2553}
2554
2555//===----------------------------------------------------------------------===//
2556// ComplexRealPtrOp
2557//===----------------------------------------------------------------------===//
2558
2559LogicalResult cir::ComplexRealPtrOp::verify() {
2560 mlir::Type resultPointeeTy = getType().getPointee();
2561 cir::PointerType operandPtrTy = getOperand().getType();
2562 auto operandPointeeTy =
2563 mlir::cast<cir::ComplexType>(operandPtrTy.getPointee());
2564
2565 if (resultPointeeTy != operandPointeeTy.getElementType()) {
2566 return emitOpError() << ": result type does not match operand type";
2567 }
2568
2569 return success();
2570}
2571
2572//===----------------------------------------------------------------------===//
2573// ComplexImagPtrOp
2574//===----------------------------------------------------------------------===//
2575
2576LogicalResult cir::ComplexImagPtrOp::verify() {
2577 mlir::Type resultPointeeTy = getType().getPointee();
2578 cir::PointerType operandPtrTy = getOperand().getType();
2579 auto operandPointeeTy =
2580 mlir::cast<cir::ComplexType>(operandPtrTy.getPointee());
2581
2582 if (resultPointeeTy != operandPointeeTy.getElementType()) {
2583 return emitOpError()
2584 << "cir.complex.imag_ptr result type does not match operand type";
2585 }
2586 return success();
2587}
2588
2589//===----------------------------------------------------------------------===//
2590// Bit manipulation operations
2591//===----------------------------------------------------------------------===//
2592
2593static OpFoldResult
2594foldUnaryBitOp(mlir::Attribute inputAttr,
2595 llvm::function_ref<llvm::APInt(const llvm::APInt &)> func,
2596 bool poisonZero = false) {
2597 if (mlir::isa_and_present<cir::PoisonAttr>(inputAttr)) {
2598 // Propagate poison value
2599 return inputAttr;
2600 }
2601
2602 auto input = mlir::dyn_cast_if_present<IntAttr>(inputAttr);
2603 if (!input)
2604 return nullptr;
2605
2606 llvm::APInt inputValue = input.getValue();
2607 if (poisonZero && inputValue.isZero())
2608 return cir::PoisonAttr::get(input.getType());
2609
2610 llvm::APInt resultValue = func(inputValue);
2611 return IntAttr::get(input.getType(), resultValue);
2612}
2613
2614OpFoldResult BitClrsbOp::fold(FoldAdaptor adaptor) {
2615 return foldUnaryBitOp(adaptor.getInput(), [](const llvm::APInt &inputValue) {
2616 unsigned resultValue =
2617 inputValue.getBitWidth() - inputValue.getSignificantBits();
2618 return llvm::APInt(inputValue.getBitWidth(), resultValue);
2619 });
2620}
2621
2622OpFoldResult BitClzOp::fold(FoldAdaptor adaptor) {
2623 return foldUnaryBitOp(
2624 adaptor.getInput(),
2625 [](const llvm::APInt &inputValue) {
2626 unsigned resultValue = inputValue.countLeadingZeros();
2627 return llvm::APInt(inputValue.getBitWidth(), resultValue);
2628 },
2629 getPoisonZero());
2630}
2631
2632OpFoldResult BitCtzOp::fold(FoldAdaptor adaptor) {
2633 return foldUnaryBitOp(
2634 adaptor.getInput(),
2635 [](const llvm::APInt &inputValue) {
2636 return llvm::APInt(inputValue.getBitWidth(),
2637 inputValue.countTrailingZeros());
2638 },
2639 getPoisonZero());
2640}
2641
2642OpFoldResult BitFfsOp::fold(FoldAdaptor adaptor) {
2643 return foldUnaryBitOp(adaptor.getInput(), [](const llvm::APInt &inputValue) {
2644 unsigned trailingZeros = inputValue.countTrailingZeros();
2645 unsigned result =
2646 trailingZeros == inputValue.getBitWidth() ? 0 : trailingZeros + 1;
2647 return llvm::APInt(inputValue.getBitWidth(), result);
2648 });
2649}
2650
2651OpFoldResult BitParityOp::fold(FoldAdaptor adaptor) {
2652 return foldUnaryBitOp(adaptor.getInput(), [](const llvm::APInt &inputValue) {
2653 return llvm::APInt(inputValue.getBitWidth(), inputValue.popcount() % 2);
2654 });
2655}
2656
2657OpFoldResult BitPopcountOp::fold(FoldAdaptor adaptor) {
2658 return foldUnaryBitOp(adaptor.getInput(), [](const llvm::APInt &inputValue) {
2659 return llvm::APInt(inputValue.getBitWidth(), inputValue.popcount());
2660 });
2661}
2662
2663OpFoldResult BitReverseOp::fold(FoldAdaptor adaptor) {
2664 return foldUnaryBitOp(adaptor.getInput(), [](const llvm::APInt &inputValue) {
2665 return inputValue.reverseBits();
2666 });
2667}
2668
2669OpFoldResult ByteSwapOp::fold(FoldAdaptor adaptor) {
2670 return foldUnaryBitOp(adaptor.getInput(), [](const llvm::APInt &inputValue) {
2671 return inputValue.byteSwap();
2672 });
2673}
2674
2675OpFoldResult RotateOp::fold(FoldAdaptor adaptor) {
2676 if (mlir::isa_and_present<cir::PoisonAttr>(adaptor.getInput()) ||
2677 mlir::isa_and_present<cir::PoisonAttr>(adaptor.getAmount())) {
2678 // Propagate poison values
2679 return cir::PoisonAttr::get(getType());
2680 }
2681
2682 auto input = mlir::dyn_cast_if_present<IntAttr>(adaptor.getInput());
2683 auto amount = mlir::dyn_cast_if_present<IntAttr>(adaptor.getAmount());
2684 if (!input && !amount)
2685 return nullptr;
2686
2687 // We could fold cir.rotate even if one of its two operands is not a constant:
2688 // - `cir.rotate left/right %0, 0` could be folded into just %0 even if %0
2689 // is not a constant.
2690 // - `cir.rotate left/right 0/0b111...111, %0` could be folded into 0 or
2691 // 0b111...111 even if %0 is not a constant.
2692
2693 llvm::APInt inputValue;
2694 if (input) {
2695 inputValue = input.getValue();
2696 if (inputValue.isZero() || inputValue.isAllOnes()) {
2697 // An input value of all 0s or all 1s will not change after rotation
2698 return input;
2699 }
2700 }
2701
2702 uint64_t amountValue;
2703 if (amount) {
2704 amountValue = amount.getValue().urem(getInput().getType().getWidth());
2705 if (amountValue == 0) {
2706 // A shift amount of 0 will not change the input value
2707 return getInput();
2708 }
2709 }
2710
2711 if (!input || !amount)
2712 return nullptr;
2713
2714 assert(inputValue.getBitWidth() == getInput().getType().getWidth() &&
2715 "input value must have the same bit width as the input type");
2716
2717 llvm::APInt resultValue;
2718 if (isRotateLeft())
2719 resultValue = inputValue.rotl(amountValue);
2720 else
2721 resultValue = inputValue.rotr(amountValue);
2722
2723 return IntAttr::get(input.getContext(), input.getType(), resultValue);
2724}
2725
2726//===----------------------------------------------------------------------===//
2727// InlineAsmOp
2728//===----------------------------------------------------------------------===//
2729
2730void cir::InlineAsmOp::print(OpAsmPrinter &p) {
2731 p << '(' << getAsmFlavor() << ", ";
2732 p.increaseIndent();
2733 p.printNewline();
2734
2735 llvm::SmallVector<std::string, 3> names{"out", "in", "in_out"};
2736 auto *nameIt = names.begin();
2737 auto *attrIt = getOperandAttrs().begin();
2738
2739 for (mlir::OperandRange ops : getAsmOperands()) {
2740 p << *nameIt << " = ";
2741
2742 p << '[';
2743 llvm::interleaveComma(llvm::make_range(ops.begin(), ops.end()), p,
2744 [&](Value value) {
2745 p.printOperand(value);
2746 p << " : " << value.getType();
2747 if (*attrIt)
2748 p << " (maybe_memory)";
2749 attrIt++;
2750 });
2751 p << "],";
2752 p.printNewline();
2753 ++nameIt;
2754 }
2755
2756 p << "{";
2757 p.printString(getAsmString());
2758 p << " ";
2759 p.printString(getConstraints());
2760 p << "}";
2761 p.decreaseIndent();
2762 p << ')';
2763 if (getSideEffects())
2764 p << " side_effects";
2765
2766 std::array elidedAttrs{
2767 llvm::StringRef("asm_flavor"), llvm::StringRef("asm_string"),
2768 llvm::StringRef("constraints"), llvm::StringRef("operand_attrs"),
2769 llvm::StringRef("operands_segments"), llvm::StringRef("side_effects")};
2770 p.printOptionalAttrDict(getOperation()->getAttrs(), elidedAttrs);
2771
2772 if (auto v = getRes())
2773 p << " -> " << v.getType();
2774}
2775
2776void cir::InlineAsmOp::build(OpBuilder &odsBuilder, OperationState &odsState,
2777 ArrayRef<ValueRange> asmOperands,
2778 StringRef asmString, StringRef constraints,
2779 bool sideEffects, cir::AsmFlavor asmFlavor,
2780 ArrayRef<Attribute> operandAttrs) {
2781 // Set up the operands_segments for VariadicOfVariadic
2782 SmallVector<int32_t> segments;
2783 for (auto operandRange : asmOperands) {
2784 segments.push_back(operandRange.size());
2785 odsState.addOperands(operandRange);
2786 }
2787
2788 odsState.addAttribute(
2789 "operands_segments",
2790 DenseI32ArrayAttr::get(odsBuilder.getContext(), segments));
2791 odsState.addAttribute("asm_string", odsBuilder.getStringAttr(asmString));
2792 odsState.addAttribute("constraints", odsBuilder.getStringAttr(constraints));
2793 odsState.addAttribute("asm_flavor",
2794 AsmFlavorAttr::get(odsBuilder.getContext(), asmFlavor));
2795
2796 if (sideEffects)
2797 odsState.addAttribute("side_effects", odsBuilder.getUnitAttr());
2798
2799 odsState.addAttribute("operand_attrs", odsBuilder.getArrayAttr(operandAttrs));
2800}
2801
2802ParseResult cir::InlineAsmOp::parse(OpAsmParser &parser,
2803 OperationState &result) {
2805 llvm::SmallVector<int32_t> operandsGroupSizes;
2806 std::string asmString, constraints;
2807 Type resType;
2808 MLIRContext *ctxt = parser.getBuilder().getContext();
2809
2810 auto error = [&](const Twine &msg) -> LogicalResult {
2811 return parser.emitError(parser.getCurrentLocation(), msg);
2812 };
2813
2814 auto expected = [&](const std::string &c) {
2815 return error("expected '" + c + "'");
2816 };
2817
2818 if (parser.parseLParen().failed())
2819 return expected("(");
2820
2821 auto flavor = FieldParser<AsmFlavor, AsmFlavor>::parse(parser);
2822 if (failed(flavor))
2823 return error("Unknown AsmFlavor");
2824
2825 if (parser.parseComma().failed())
2826 return expected(",");
2827
2828 auto parseValue = [&](Value &v) {
2829 OpAsmParser::UnresolvedOperand op;
2830
2831 if (parser.parseOperand(op) || parser.parseColon())
2832 return error("can't parse operand");
2833
2834 Type typ;
2835 if (parser.parseType(typ).failed())
2836 return error("can't parse operand type");
2838 if (parser.resolveOperand(op, typ, tmp))
2839 return error("can't resolve operand");
2840 v = tmp[0];
2841 return mlir::success();
2842 };
2843
2844 auto parseOperands = [&](llvm::StringRef name) {
2845 if (parser.parseKeyword(name).failed())
2846 return error("expected " + name + " operands here");
2847 if (parser.parseEqual().failed())
2848 return expected("=");
2849 if (parser.parseLSquare().failed())
2850 return expected("[");
2851
2852 int size = 0;
2853 if (parser.parseOptionalRSquare().succeeded()) {
2854 operandsGroupSizes.push_back(size);
2855 if (parser.parseComma())
2856 return expected(",");
2857 return mlir::success();
2858 }
2859
2860 auto parseOperand = [&]() {
2861 Value val;
2862 if (parseValue(val).succeeded()) {
2863 result.operands.push_back(val);
2864 size++;
2865
2866 if (parser.parseOptionalLParen().failed()) {
2867 operandAttrs.push_back(mlir::Attribute());
2868 return mlir::success();
2869 }
2870
2871 if (parser.parseKeyword("maybe_memory").succeeded()) {
2872 operandAttrs.push_back(mlir::UnitAttr::get(ctxt));
2873 if (parser.parseRParen())
2874 return expected(")");
2875 return mlir::success();
2876 } else {
2877 return expected("maybe_memory");
2878 }
2879 }
2880 return mlir::failure();
2881 };
2882
2883 if (parser.parseCommaSeparatedList(parseOperand).failed())
2884 return mlir::failure();
2885
2886 if (parser.parseRSquare().failed() || parser.parseComma().failed())
2887 return expected("]");
2888 operandsGroupSizes.push_back(size);
2889 return mlir::success();
2890 };
2891
2892 if (parseOperands("out").failed() || parseOperands("in").failed() ||
2893 parseOperands("in_out").failed())
2894 return error("failed to parse operands");
2895
2896 if (parser.parseLBrace())
2897 return expected("{");
2898 if (parser.parseString(&asmString))
2899 return error("asm string parsing failed");
2900 if (parser.parseString(&constraints))
2901 return error("constraints string parsing failed");
2902 if (parser.parseRBrace())
2903 return expected("}");
2904 if (parser.parseRParen())
2905 return expected(")");
2906
2907 if (parser.parseOptionalKeyword("side_effects").succeeded())
2908 result.attributes.set("side_effects", UnitAttr::get(ctxt));
2909
2910 if (parser.parseOptionalArrow().succeeded() &&
2911 parser.parseType(resType).failed())
2912 return mlir::failure();
2913
2914 if (parser.parseOptionalAttrDict(result.attributes).failed())
2915 return mlir::failure();
2916
2917 result.attributes.set("asm_flavor", AsmFlavorAttr::get(ctxt, *flavor));
2918 result.attributes.set("asm_string", StringAttr::get(ctxt, asmString));
2919 result.attributes.set("constraints", StringAttr::get(ctxt, constraints));
2920 result.attributes.set("operand_attrs", ArrayAttr::get(ctxt, operandAttrs));
2921 result.getOrAddProperties<InlineAsmOp::Properties>().operands_segments =
2922 parser.getBuilder().getDenseI32ArrayAttr(operandsGroupSizes);
2923 if (resType)
2924 result.addTypes(TypeRange{resType});
2925
2926 return mlir::success();
2927}
2928
2929//===----------------------------------------------------------------------===//
2930// ThrowOp
2931//===----------------------------------------------------------------------===//
2932
2933mlir::LogicalResult cir::ThrowOp::verify() {
2934 // For the no-rethrow version, it must have at least the exception pointer.
2935 if (rethrows())
2936 return success();
2937
2938 if (getNumOperands() != 0) {
2939 if (getTypeInfo())
2940 return success();
2941 return emitOpError() << "'type_info' symbol attribute missing";
2942 }
2943
2944 return failure();
2945}
2946
2947//===----------------------------------------------------------------------===//
2948// AtomicFetchOp
2949//===----------------------------------------------------------------------===//
2950
2951LogicalResult cir::AtomicFetchOp::verify() {
2952 if (getBinop() != cir::AtomicFetchKind::Add &&
2953 getBinop() != cir::AtomicFetchKind::Sub &&
2954 getBinop() != cir::AtomicFetchKind::Max &&
2955 getBinop() != cir::AtomicFetchKind::Min &&
2956 !mlir::isa<cir::IntType>(getVal().getType()))
2957 return emitError("only atomic add, sub, max, and min operation could "
2958 "operate on floating-point values");
2959 return success();
2960}
2961
2962//===----------------------------------------------------------------------===//
2963// TypeInfoAttr
2964//===----------------------------------------------------------------------===//
2965
2966LogicalResult cir::TypeInfoAttr::verify(
2967 ::llvm::function_ref<::mlir::InFlightDiagnostic()> emitError,
2968 ::mlir::Type type, ::mlir::ArrayAttr typeInfoData) {
2969
2970 if (cir::ConstRecordAttr::verify(emitError, type, typeInfoData).failed())
2971 return failure();
2972
2973 return success();
2974}
2975
2976//===----------------------------------------------------------------------===//
2977// TryOp
2978//===----------------------------------------------------------------------===//
2979
2980void cir::TryOp::getSuccessorRegions(
2981 mlir::RegionBranchPoint point,
2983 // The `try` and the `catchers` region branch back to the parent operation.
2984 if (!point.isParent()) {
2985 regions.push_back(
2986 RegionSuccessor(getOperation(), getOperation()->getResults()));
2987 return;
2988 }
2989
2990 regions.push_back(mlir::RegionSuccessor(&getTryRegion()));
2991
2992 // TODO(CIR): If we know a target function never throws a specific type, we
2993 // can remove the catch handler.
2994 for (mlir::Region &handlerRegion : this->getHandlerRegions())
2995 regions.push_back(mlir::RegionSuccessor(&handlerRegion));
2996}
2997
2998static void
2999printTryHandlerRegions(mlir::OpAsmPrinter &printer, cir::TryOp op,
3000 mlir::MutableArrayRef<mlir::Region> handlerRegions,
3001 mlir::ArrayAttr handlerTypes) {
3002 if (!handlerTypes)
3003 return;
3004
3005 for (const auto [typeIdx, typeAttr] : llvm::enumerate(handlerTypes)) {
3006 if (typeIdx)
3007 printer << " ";
3008
3009 if (mlir::isa<cir::CatchAllAttr>(typeAttr)) {
3010 printer << "catch all ";
3011 } else if (mlir::isa<cir::UnwindAttr>(typeAttr)) {
3012 printer << "unwind ";
3013 } else {
3014 printer << "catch [type ";
3015 printer.printAttribute(typeAttr);
3016 printer << "] ";
3017 }
3018
3019 printer.printRegion(handlerRegions[typeIdx],
3020 /*printEntryBLockArgs=*/false,
3021 /*printBlockTerminators=*/true);
3022 }
3023}
3024
3025static mlir::ParseResult parseTryHandlerRegions(
3026 mlir::OpAsmParser &parser,
3027 llvm::SmallVectorImpl<std::unique_ptr<mlir::Region>> &handlerRegions,
3028 mlir::ArrayAttr &handlerTypes) {
3029
3030 auto parseCheckedCatcherRegion = [&]() -> mlir::ParseResult {
3031 handlerRegions.emplace_back(new mlir::Region);
3032
3033 mlir::Region &currRegion = *handlerRegions.back();
3034 mlir::SMLoc regionLoc = parser.getCurrentLocation();
3035 if (parser.parseRegion(currRegion)) {
3036 handlerRegions.clear();
3037 return failure();
3038 }
3039
3040 if (currRegion.empty())
3041 return parser.emitError(regionLoc, "handler region shall not be empty");
3042
3043 if (!(currRegion.back().mightHaveTerminator() &&
3044 currRegion.back().getTerminator()))
3045 return parser.emitError(
3046 regionLoc, "blocks are expected to be explicitly terminated");
3047
3048 return success();
3049 };
3050
3051 bool hasCatchAll = false;
3053 while (parser.parseOptionalKeyword("catch").succeeded()) {
3054 bool hasLSquare = parser.parseOptionalLSquare().succeeded();
3055
3056 llvm::StringRef attrStr;
3057 if (parser.parseOptionalKeyword(&attrStr, {"all", "type"}).failed())
3058 return parser.emitError(parser.getCurrentLocation(),
3059 "expected 'all' or 'type' keyword");
3060
3061 bool isCatchAll = attrStr == "all";
3062 if (isCatchAll) {
3063 if (hasCatchAll)
3064 return parser.emitError(parser.getCurrentLocation(),
3065 "can't have more than one catch all");
3066 hasCatchAll = true;
3067 }
3068
3069 mlir::Attribute exceptionRTTIAttr;
3070 if (!isCatchAll && parser.parseAttribute(exceptionRTTIAttr).failed())
3071 return parser.emitError(parser.getCurrentLocation(),
3072 "expected valid RTTI info attribute");
3073
3074 catcherAttrs.push_back(isCatchAll
3075 ? cir::CatchAllAttr::get(parser.getContext())
3076 : exceptionRTTIAttr);
3077
3078 if (hasLSquare && isCatchAll)
3079 return parser.emitError(parser.getCurrentLocation(),
3080 "catch all dosen't need RTTI info attribute");
3081
3082 if (hasLSquare && parser.parseRSquare().failed())
3083 return parser.emitError(parser.getCurrentLocation(),
3084 "expected `]` after RTTI info attribute");
3085
3086 if (parseCheckedCatcherRegion().failed())
3087 return mlir::failure();
3088 }
3089
3090 if (parser.parseOptionalKeyword("unwind").succeeded()) {
3091 if (hasCatchAll)
3092 return parser.emitError(parser.getCurrentLocation(),
3093 "unwind can't be used with catch all");
3094
3095 catcherAttrs.push_back(cir::UnwindAttr::get(parser.getContext()));
3096 if (parseCheckedCatcherRegion().failed())
3097 return mlir::failure();
3098 }
3099
3100 handlerTypes = parser.getBuilder().getArrayAttr(catcherAttrs);
3101 return mlir::success();
3102}
3103
3104//===----------------------------------------------------------------------===//
3105// TableGen'd op method definitions
3106//===----------------------------------------------------------------------===//
3107
3108#define GET_OP_CLASSES
3109#include "clang/CIR/Dialect/IR/CIROps.cpp.inc"
static const MemRegion * getRegion(const CallEvent &Call, const MutexDescriptor &Descriptor, bool IsLock)
static mlir::LogicalResult checkReturnAndFunction(cir::ReturnOp op, cir::FuncOp function)
static bool isBoolNot(cir::UnaryOp op)
static bool isIntOrBoolCast(cir::CastOp op)
static void printConstant(OpAsmPrinter &p, Attribute value)
static mlir::ParseResult parseOmittedTerminatorRegion(mlir::OpAsmParser &parser, mlir::Region &region)
void printVisibilityAttr(OpAsmPrinter &printer, cir::VisibilityAttr &visibility)
static ParseResult parseSwitchFlatOpCases(OpAsmParser &parser, Type flagType, mlir::ArrayAttr &caseValues, SmallVectorImpl< Block * > &caseDestinations, SmallVectorImpl< llvm::SmallVector< OpAsmParser::UnresolvedOperand > > &caseOperands, SmallVectorImpl< llvm::SmallVector< Type > > &caseOperandTypes)
<cases> ::= [ (case (, case )* )?
static LogicalResult verifyCallCommInSymbolUses(mlir::Operation *op, SymbolTableCollection &symbolTable)
static LogicalResult ensureRegionTerm(OpAsmParser &parser, Region &region, SMLoc errLoc)
static ParseResult parseGlobalOpTypeAndInitialValue(OpAsmParser &parser, TypeAttr &typeAttr, Attribute &initialValueAttr, mlir::Region &ctorRegion, mlir::Region &dtorRegion)
static OpFoldResult foldUnaryBitOp(mlir::Attribute inputAttr, llvm::function_ref< llvm::APInt(const llvm::APInt &)> func, bool poisonZero=false)
static llvm::StringRef getLinkageAttrNameString()
Returns the name used for the linkage attribute.
static RetTy parseOptionalCIRKeyword(AsmParser &parser, EnumTy defaultValue)
Parse an enum from the keyword, or default to the provided default value.
static void printSwitchFlatOpCases(OpAsmPrinter &p, cir::SwitchFlatOp op, Type flagType, mlir::ArrayAttr caseValues, SuccessorRange caseDestinations, OperandRangeRange caseOperands, const TypeRangeRange &caseOperandTypes)
static void printSwitchOp(OpAsmPrinter &p, cir::SwitchOp op, mlir::Region &bodyRegion, mlir::Value condition, mlir::Type condType)
static void printGlobalOpTypeAndInitialValue(OpAsmPrinter &p, cir::GlobalOp op, TypeAttr type, Attribute initAttr, mlir::Region &ctorRegion, mlir::Region &dtorRegion)
static ParseResult parseCIRKeyword(AsmParser &parser, RetTy &result)
Parse an enum from the keyword, return failure if the keyword is not found.
static Value tryFoldCastChain(cir::CastOp op)
void parseVisibilityAttr(OpAsmParser &parser, cir::VisibilityAttr &visibility)
static void printTryHandlerRegions(mlir::OpAsmPrinter &printer, cir::TryOp op, mlir::MutableArrayRef< mlir::Region > handlerRegions, mlir::ArrayAttr handlerTypes)
static bool omitRegionTerm(mlir::Region &r)
static void printOmittedTerminatorRegion(mlir::OpAsmPrinter &printer, cir::ScopeOp &op, mlir::Region &region)
static ParseResult parseConstantValue(OpAsmParser &parser, mlir::Attribute &valueAttr)
static void printCallCommon(mlir::Operation *op, mlir::FlatSymbolRefAttr calleeSym, mlir::Value indirectCallee, mlir::OpAsmPrinter &printer, bool isNothrow, cir::SideEffect sideEffect)
static LogicalResult checkConstantTypes(mlir::Operation *op, mlir::Type opType, mlir::Attribute attrType)
static ParseResult parseSwitchOp(OpAsmParser &parser, mlir::Region &regions, mlir::OpAsmParser::UnresolvedOperand &cond, mlir::Type &condType)
static mlir::ParseResult parseCallCommon(mlir::OpAsmParser &parser, mlir::OperationState &result)
static mlir::ParseResult parseTryHandlerRegions(mlir::OpAsmParser &parser, llvm::SmallVectorImpl< std::unique_ptr< mlir::Region > > &handlerRegions, mlir::ArrayAttr &handlerTypes)
#define REGISTER_ENUM_TYPE(Ty)
static int parseOptionalKeywordAlternative(AsmParser &parser, ArrayRef< llvm::StringRef > keywords)
llvm::function_ref< void(mlir::OpBuilder &, mlir::Location)> BuilderCallbackRef
Definition CIRDialect.h:37
llvm::function_ref< void( mlir::OpBuilder &, mlir::Location, mlir::OperationState &)> BuilderOpStateCallbackRef
Definition CIRDialect.h:39
static std::optional< NonLoc > getIndex(ProgramStateRef State, const ElementRegion *ER, CharKind CK)
static Decl::Kind getKind(const Decl *D)
TokenType getType() const
Returns the token's type, e.g.
__device__ __2f16 float c
void buildTerminatedBody(mlir::OpBuilder &builder, mlir::Location loc)
const AstTypeMatcher< RecordType > recordType
StringRef getName(const HeaderType T)
Definition HeaderFile.h:38
RangeSelector name(std::string ID)
Given a node with a "name", (like NamedDecl, DeclRefExpr, CxxCtorInitializer, and TypeLoc) selects th...
nullptr
This class represents a compute construct, representing a 'Kind' of ‘parallel’, 'serial',...
__DEVICE__ _Tp arg(const std::complex< _Tp > &__c)
static bool addressSpace()
static bool opGlobalThreadLocal()
static bool opCallCallConv()
static bool opScopeCleanupRegion()
static bool supportIFuncAttr()