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