// // This file is distributed under the MIT License. See LICENSE.md for details. // #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallSet.h" #include "mlir/IR/FunctionImplementation.h" #include "mlir/IR/RegionGraphTraits.h" #include "revng/Clift/Clift.h" #include "revng/Clift/CliftOpHelpers.h" using UnresolvedOperand = mlir::OpAsmParser::UnresolvedOperand; namespace mlir { static ParseResult parseCliftLoopLabels(OpAsmParser &Parser, mlir::IntegerAttr &LabelMask, llvm::SmallVectorImpl &Labels); static void printCliftLoopLabels(OpAsmPrinter &Printer, mlir::Operation *Op, mlir::IntegerAttr LabelMask, mlir::OperandRange Labels); static ParseResult parseCliftOpTypesImpl(OpAsmParser &Parser, Type *Result, llvm::ArrayRef Arguments); static void printCliftOpTypesImpl(OpAsmPrinter &Printer, Type Result, llvm::ArrayRef Arguments); template... Ts> static ParseResult parseCliftOpTypes(OpAsmParser &Parser, Type &Result, Ts &...Arguments) { static_assert(sizeof...(Ts) > 0); return parseCliftOpTypesImpl(Parser, &Result, { &Arguments... }); } template... Ts> static ParseResult parseCliftOpOperandTypes(OpAsmParser &Parser, Ts &...Arguments) { static_assert(sizeof...(Ts) > 0); return parseCliftOpTypesImpl(Parser, nullptr, { &Arguments... }); } template... Ts> static void printCliftOpTypes(OpAsmPrinter &Printer, Operation *Op, Type Result, Ts... Arguments) { static_assert(sizeof...(Ts) > 0); printCliftOpTypesImpl(Printer, Result, { Arguments... }); } template... Ts> static void printCliftOpOperandTypes(OpAsmPrinter &Printer, Operation *Op, Ts... Arguments) { static_assert(sizeof...(Ts) > 0); printCliftOpTypesImpl(Printer, nullptr, { Arguments... }); } static ParseResult parseCliftPointerArithmeticOpTypes(OpAsmParser &Parser, Type &Result, Type &Lhs, Type &Rhs); static void printCliftPointerArithmeticOpTypes(OpAsmPrinter &Parser, Operation *Op, Type Result, Type Lhs, Type Rhs); static ParseResult parseCliftTernaryOpTypes(OpAsmParser &Parser, Type &Condition, Type &Lhs, Type &Rhs); static void printCliftTernaryOpTypes(OpAsmPrinter &Printer, Operation *Op, Type Condition, Type Lhs, Type Rhs); } // namespace mlir #define GET_OP_CLASSES #include "revng/Clift/Clift.cpp.inc" using namespace mlir; using namespace mlir::clift; void CliftDialect::registerOperations() { addOperations(); } bool clift::hasModuleAttr(mlir::ModuleOp Module) { llvm::StringRef AttrName = CliftDialect::getModuleAttrName(); return Module->hasAttrOfType(AttrName); } void clift::setModuleAttr(mlir::ModuleOp Module) { Module->setAttr(CliftDialect::getModuleAttrName(), mlir::UnitAttr::get(Module.getContext())); } YieldOp clift::getExpressionYieldOp(Region &R) { if (R.empty()) return {}; Block &B = R.front(); if (B.empty()) return {}; return mlir::dyn_cast(B.back()); } mlir::Value clift::getExpressionValue(Region &R) { if (auto Yield = getExpressionYieldOp(R)) return Yield.getValue(); return {}; } ValueType clift::getExpressionType(Region &R) { if (auto Value = getExpressionValue(R)) return mlir::cast(Value.getType()); return {}; } //===-------------------------- Type constraints --------------------------===// bool clift::impl::verifyPrimitiveTypeOf(ValueType Type, PrimitiveKind Kind) { if (auto T = mlir::dyn_cast(Type)) return T.getKind() == Kind; return false; } //===---------------------------- Region types ----------------------------===// template static bool verifyRegionContent(Region &R, const bool Required) { if (R.empty()) return not Required; if (not R.hasOneBlock()) return false; for (Operation &Op : R.front()) { if (not mlir::isa(&Op)) return false; } return true; } bool clift::impl::verifyStatementRegion(Region &R) { return verifyRegionContent(R, false); } bool clift::impl::verifyExpressionRegion(Region &R, const bool Required) { if (not verifyRegionContent(R, Required)) return false; return R.empty() or static_cast(getExpressionYieldOp(R)); } //===-------------------------- Operation parsing -------------------------===// template static Type deduceResultType(llvm::ArrayRef Arguments) { const auto getType = [](TypeOrPointer Argument) -> ValueType { if constexpr (std::is_same_v) { return mlir::cast(Argument); } else { return mlir::cast(*Argument); } }; auto CommonType = getType(Arguments.front()).removeConst(); for (TypeOrPointer Argument : Arguments.slice(0)) { if (getType(Argument).removeConst() != CommonType) return {}; } return CommonType; } /// Parses one or more operand types optionally followed by a result type. /// /// The argument types can be specified in two forms: /// * a single type, or /// * one or more types separated by commas and delimited by parentheses. /// /// If no parentheses are used, the single specified argument type is used for /// all expected argument types. Otherwise the number of specified argument /// types must match the number of expected argument types. /// /// The trailing result type is only accepted when @p Result is not null. When /// the result type is not specified, a default type is deduced by taking each /// argument types T and removing const to produce the unqualified type U. If /// all U are equal, then U is deduced. Otherwise the deduction is ambiguous /// and the parse fails. /// /// Examples: /// - !a /// - !a -> !c /// - (!a, !b) /// - (!a, !b) -> !c ParseResult mlir::parseCliftOpTypesImpl(OpAsmParser &Parser, Type *Result, llvm::ArrayRef Arguments) { Type &FirstArgument = *Arguments.front(); if (Parser.parseOptionalLParen().succeeded()) { if (Parser.parseType(FirstArgument).failed()) return mlir::failure(); for (Type *Argument : Arguments.slice(1)) { if (Parser.parseComma().failed()) return mlir::failure(); if (Parser.parseType(*Argument).failed()) return mlir::failure(); } if (Parser.parseRParen().failed()) return mlir::failure(); } else { if (Parser.parseType(FirstArgument).failed()) return mlir::failure(); for (Type *Argument : Arguments.slice(1)) *Argument = FirstArgument; } if (Result != nullptr) { if (Parser.parseOptionalArrow().succeeded()) { if (Parser.parseType(*Result).failed()) return mlir::failure(); } else if (ValueType Deduced = deduceResultType(Arguments)) { *Result = Deduced; } else { return Parser.emitError(Parser.getCurrentLocation(), "expected arrow followed by result type"); } } return mlir::success(); } /// @see parseCliftOpTypes for a general description of the syntax. /// /// If all argument types are equal, a single argument is printed. Otherwise /// multiple arguments delimited by parentheses are printed. /// /// If @p Result is not null and it cannot be deduced from the argument types, /// a trailing type is printed. void mlir::printCliftOpTypesImpl(OpAsmPrinter &Printer, Type Result, llvm::ArrayRef Arguments) { bool ArgumentsEqual = llvm::all_equal(Arguments); Type FirstArgument = Arguments.front(); if (ArgumentsEqual) { Printer << FirstArgument; } else { Printer << "("; Printer << FirstArgument; for (Type Argument : Arguments.slice(1)) { Printer << ", "; Printer << Argument; } Printer << ")"; } if (Result) { bool IsDeducible = false; if (ArgumentsEqual) { if (Result == FirstArgument) { IsDeducible = true; } else { auto FirstArgumentT = mlir::cast(FirstArgument); IsDeducible = Result == FirstArgumentT.removeConst(); } } else { IsDeducible = Result == deduceResultType(Arguments); } if (not IsDeducible) { Printer << " -> "; Printer << Result; } } } //===----------------------------- FunctionOp -----------------------------===// void FunctionOp::build(OpBuilder &Builder, OperationState &State, llvm::StringRef Name, clift::FunctionType FunctionType) { size_t ArgumentCount = FunctionType.getArgumentTypes().size(); llvm::SmallVector Array; Array.resize(std::max(ArgumentCount, 1), mlir::DictionaryAttr::get(Builder.getContext())); auto GetArrayAttr = [&](unsigned Count) { return mlir::ArrayAttr::get(Builder.getContext(), llvm::ArrayRef(Array).take_front(Count)); }; build(Builder, State, Name, FunctionType, /*arg_attrs=*/GetArrayAttr(ArgumentCount), /*res_attrs=*/GetArrayAttr(1)); } mlir::ParseResult FunctionOp::parse(OpAsmParser &Parser, OperationState &Result) { StringAttr SymbolNameAttr; if (Parser .parseSymbolName(SymbolNameAttr, SymbolTable::getSymbolAttrName(), Result.attributes) .failed()) return mlir::failure(); if (Parser.parseLess().failed()) return mlir::failure(); auto FunctionTypeLoc = Parser.getCurrentLocation(); clift::ValueType Type; if (Parser.parseType(Type).failed()) return mlir::failure(); auto FunctionType = mlir::dyn_cast(Type); if (not FunctionType) return Parser.emitError(FunctionTypeLoc) << "expected Clift function or " "pointer-to-function type."; if (Parser.parseGreater().failed()) return mlir::failure(); llvm::SmallVector Arguments; llvm::SmallVector ResultTypes; llvm::SmallVector ResultAttrs; bool IsVariadic = false; auto RoughResultTypeLocation = Parser.getCurrentLocation(); if (function_interface_impl::parseFunctionSignature(Parser, /*allowVariadic=*/false, Arguments, IsVariadic, ResultTypes, ResultAttrs) .failed()) return mlir::failure(); if (ResultTypes.size() > 1) return Parser.emitError(RoughResultTypeLocation) << "expected no more than " "one result"; if (ResultTypes.empty()) { ResultTypes.push_back(PrimitiveType::get(Parser.getContext(), PrimitiveKind::VoidKind, 0, false)); ResultAttrs.push_back(DictionaryAttr::get(Parser.getContext())); } llvm::SmallVector ArgumentTypes; for (auto &Argument : Arguments) ArgumentTypes.push_back(Argument.type); Result.addAttribute(getFunctionTypeAttrName(Result.name), TypeAttr::get(FunctionType)); if (Parser.parseOptionalAttrDictWithKeyword(Result.attributes).failed()) return mlir::failure(); function_interface_impl::addArgAndResultAttrs(Parser.getBuilder(), Result, Arguments, ResultAttrs, getArgAttrsAttrName(Result .name), getResAttrsAttrName(Result .name)); auto *Body = Result.addRegion(); auto RegionParseResult = Parser.parseOptionalRegion(*Body, Arguments); if (RegionParseResult.has_value() && mlir::failed(*RegionParseResult)) return mlir::failure(); return mlir::success(); } void FunctionOp::print(OpAsmPrinter &Printer) { auto FunctionType = getFunctionType(); Printer << ' '; Printer.printSymbolName(getSymName()); Printer << '<'; Printer.printType(FunctionType); Printer << '>'; function_interface_impl::printFunctionSignature(Printer, *this, FunctionType .getArgumentTypes(), /*isVariadic=*/false, FunctionType .getResultTypes()); function_interface_impl::printFunctionAttributes(Printer, *this, { getFunctionTypeAttrName(), getArgAttrsAttrName(), getResAttrsAttrName() }); if (Region &Body = getBody(); !Body.empty()) { Printer << ' '; Printer.printRegion(Body, /*printEntryBlockArgs=*/false, /*printBlockTerminators=*/true); } } mlir::LogicalResult FunctionOp::verify() { auto ReturnType = getReturnType(); bool IsVoid = isVoid(ReturnType); auto Result = (*this)->walk([&](ReturnOp Op) -> mlir::WalkResult { clift::ValueType Type = getExpressionType(Op.getResult()); if (IsVoid) { if (Type) return Op->emitOpError() << "cannot return expression in function " "returning void."; } else if (not Type) { return Op->emitOpError() << "must return a value in function not " "returning void."; } else if (Type != ReturnType) { return Op->emitOpError() << "type does not match the function return " "type"; } return mlir::success(); }); return mlir::failure(Result.wasInterrupted()); } ArrayRef FunctionOp::getArgumentTypes() { return getFunctionType().getArgumentTypes(); } ArrayRef FunctionOp::getResultTypes() { return getFunctionType().getResultTypes(); } Type FunctionOp::cloneTypeWith(TypeRange inputs, TypeRange results) { revng_abort("Operation not supported"); } //===-------------------------- GlobalVariableOp --------------------------===// mlir::LogicalResult GlobalVariableOp::verify() { if (Region &R = getInitializer(); not R.empty()) { if (getExpressionType(R) != getType()) return emitOpError() << getOperationName() << " initializer type must match the variable type"; } return mlir::success(); } //===----------------------------- Statements -----------------------------===// static mlir::IntegerAttr makeLoopLabelMask(mlir::MLIRContext *Context, unsigned Mask) { return mlir::IntegerAttr::get(Context, llvm::APSInt(llvm::APInt(2, Mask))); } /// If \p OtherLoop is non-null, assigned label operands are copied from it. /// This is useful for any transforms which change the type of a loop, and must /// preserve the assigned labels. static void buildLoop(OpBuilder &Builder, OperationState &State, unsigned RegionCount, LoopOpInterface OtherLoop = {}) { if (OtherLoop) { State.addOperands(OtherLoop->getOperands()); State.addAttribute(clift::impl::LoopLabelMaskAttrName, OtherLoop->getAttr(clift::impl::LoopLabelMaskAttrName)); } else { State.addAttribute(clift::impl::LoopLabelMaskAttrName, makeLoopLabelMask(Builder.getContext(), 0)); } for (unsigned I = 0; I < RegionCount; ++I) State.addRegion(); } ParseResult mlir::parseCliftLoopLabels(OpAsmParser &Parser, mlir::IntegerAttr &LabelMask, llvm::SmallVectorImpl &Labels) { unsigned Mask = 0; if (Parser.parseOptionalKeyword("break").succeeded()) { UnresolvedOperand Operand; if (Parser.parseOperand(Operand).failed()) return mlir::failure(); Labels.push_back(Operand); Mask |= clift::impl::BreakLabelFlag; } if (Parser.parseOptionalKeyword("continue").succeeded()) { UnresolvedOperand Operand; if (Parser.parseOperand(Operand).failed()) return mlir::failure(); Labels.push_back(Operand); Mask |= clift::impl::ContinueLabelFlag; } LabelMask = makeLoopLabelMask(Parser.getContext(), Mask); return mlir::success(); } void mlir::printCliftLoopLabels(OpAsmPrinter &Printer, mlir::Operation *Op, mlir::IntegerAttr LabelMask, mlir::OperandRange Labels) { unsigned Mask = LabelMask.getValue().getZExtValue(); unsigned Next = 0; if (Mask & clift::impl::BreakLabelFlag) { Printer << "break "; Printer.printOperand(Labels[Next++]); } if (Mask & clift::impl::ContinueLabelFlag) { if (Next != 0) Printer << " "; Printer << "continue "; Printer.printOperand(Labels[Next++]); } } //===---------------------------- AssignLabelOp ---------------------------===// MakeLabelOp AssignLabelOp::getLabelOp() { return getLabel().getDefiningOp(); } //===-------------------------- BlockStatementOp --------------------------===// bool BlockStatementOp::isIndirectlyNoFallthrough() { return clift::isIndirectlyNoFallthrough(getBlock()); } //===------------------------------ BreakToOp -----------------------------===// mlir::LogicalResult BreakToOp::verify() { mlir::Operation *Assignment = getLabelAssignmentOp(); auto Loop = mlir::dyn_cast(Assignment); if (not Loop or getLabel() != Loop.getBreakLabel()) { return emitOpError() << getOperationName() << " must target a loop break label."; } if (not Loop->isAncestor(getOperation())) { return emitOpError() << getOperationName() << " must target a nesting loop label."; } return mlir::success(); } //===---------------------------- ContinueToOp ----------------------------===// mlir::LogicalResult ContinueToOp::verify() { mlir::Operation *Assignment = getLabelAssignmentOp(); auto Loop = mlir::dyn_cast(Assignment); if (not Loop or getLabel() != Loop.getContinueLabel()) { return emitOpError() << getOperationName() << " must target a loop continue label."; } if (not Loop->isAncestor(getOperation())) { return emitOpError() << getOperationName() << " must target a nesting loop label."; } return mlir::success(); } //===------------------------------ DoWhileOp -----------------------------===// void DoWhileOp::build(OpBuilder &Builder, OperationState &State, LoopOpInterface OtherLoop) { buildLoop(Builder, State, 2, OtherLoop); } mlir::LogicalResult DoWhileOp::verify() { if (not isScalarType(getExpressionType(getCondition()))) return emitOpError() << getOperationName() << " condition requires a scalar type."; return mlir::success(); } //===-------------------------------- ForOp -------------------------------===// bool ForOp::isDiscardedExpression(mlir::Region &R) { if (&R == &getInitializer()) return not getOnlyOp(R); return &R == &getExpression(); } void ForOp::build(OpBuilder &Builder, OperationState &State, LoopOpInterface OtherLoop) { buildLoop(Builder, State, 4, OtherLoop); } mlir::ParseResult ForOp::parse(OpAsmParser &Parser, OperationState &Result) { mlir::IntegerAttr LabelMaskAttr; llvm::SmallVector LabelOperands; llvm::SMLoc LabelOperandsLoc = Parser.getCurrentLocation(); if (parseCliftLoopLabels(Parser, LabelMaskAttr, LabelOperands)) return mlir::failure(); mlir::Type InitType = {}; auto InitRegion = std::make_unique(); if (Parser.parseOptionalKeyword("init").succeeded()) { if (Parser.parseOptionalColon().succeeded()) { if (Parser.parseType(InitType).failed()) return mlir::failure(); } if (Parser.parseRegion(*InitRegion).failed()) return mlir::failure(); } auto ParseRegion = [&Parser, &InitType](mlir::Region &R) -> mlir::LogicalResult { llvm::SmallVector Arguments; llvm::SMLoc OperandLoc = Parser.getCurrentLocation(); UnresolvedOperand Operand; if (Parser.parseOptionalLParen().succeeded()) { if (Parser.parseOperand(Operand).failed()) return mlir::failure(); if (Parser.parseRParen().failed()) return mlir::failure(); if (not InitType) { return Parser.emitError(OperandLoc, "Region operand requires specifying the type " "of the variable declared in the init region " "of this operation."); } Arguments.emplace_back(Operand, InitType); } if (Parser.parseRegion(R, Arguments).failed()) return mlir::failure(); return mlir::success(); }; auto CondRegion = std::make_unique(); if (Parser.parseOptionalKeyword("cond").succeeded()) { if (ParseRegion(*CondRegion).failed()) return mlir::failure(); } auto NextRegion = std::make_unique(); if (Parser.parseOptionalKeyword("next").succeeded()) { if (ParseRegion(*NextRegion).failed()) return mlir::failure(); } if (Parser.parseKeyword("body").failed()) return mlir::failure(); auto BodyRegion = std::make_unique(); if (ParseRegion(*BodyRegion).failed()) return mlir::failure(); if (Parser.parseOptionalAttrDictWithKeyword(Result.attributes)) return mlir::failure(); if (Parser .resolveOperands(LabelOperands, LabelType::get(Parser.getContext()), LabelOperandsLoc, Result.operands) .failed()) return mlir::failure(); Result.addAttribute(clift::impl::LoopLabelMaskAttrName, LabelMaskAttr); Result.addRegion(std::move(InitRegion)); Result.addRegion(std::move(CondRegion)); Result.addRegion(std::move(NextRegion)); Result.addRegion(std::move(BodyRegion)); return mlir::success(); } void ForOp::print(OpAsmPrinter &Printer) { mlir::Type InitType = {}; auto SetInitType = [&InitType](mlir::Region &R) { if (not InitType and R.getNumArguments() != 0) InitType = R.getArgument(0).getType(); }; SetInitType(getCondition()); SetInitType(getExpression()); SetInitType(getBody()); Printer << ' '; printCliftLoopLabels(Printer, *this, getLabelMaskAttr(), getLabels()); Printer << ' '; if (InitType or not getInitializer().empty()) { Printer << " init "; if (InitType) { Printer << ':'; Printer << ' '; Printer.printType(InitType); Printer << ' '; } Printer.printRegion(getInitializer()); } auto PrintRegion = [&Printer](mlir::Region &R) { if (R.getNumArguments() != 0) { Printer << '('; Printer.printOperand(R.getArgument(0)); Printer << ')'; Printer << ' '; } Printer.printRegion(R, /*printEntryBlockArgs=*/false); }; if (not getCondition().empty()) { Printer << " cond "; PrintRegion(getCondition()); } if (not getExpression().empty()) { Printer << " next "; PrintRegion(getExpression()); } Printer << " body "; PrintRegion(getBody()); Printer.printOptionalAttrDictWithKeyword(getOperation()->getAttrs(), clift::impl::LoopLabelMaskAttrName); } mlir::LogicalResult ForOp::verify() { Region &Initializer = getInitializer(); clift::ValueType InitType = {}; if (not Initializer.empty()) { mlir::Operation *Op = getOnlyOp(Initializer); if (not Op or not mlir::isa(Op)) { return emitOpError() << getOperationName() << " initializer region must be empty or contain" " exactly one expression statement or local" " variable declaration."; } if (auto Local = mlir::dyn_cast(Op)) InitType = Local.getType(); } auto CheckRegionArguments = [this, &InitType](llvm::StringRef Name, mlir::Region &R) -> mlir::LogicalResult { if (R.getNumArguments() != 0) { if (R.getNumArguments() != 1) { return emitOpError() << getOperationName() << " " << Name << " region may have no more than one argument."; } if (R.getArgument(0).getType() != InitType) { return emitOpError() << getOperationName() << " " << Name << " region argument type must match the type of" " the local variable declaration contained in " " the init region."; } } return mlir::success(); }; if (CheckRegionArguments("condition", getCondition()).failed()) return mlir::failure(); if (CheckRegionArguments("expression", getExpression()).failed()) return mlir::failure(); if (CheckRegionArguments("body", getBody()).failed()) return mlir::failure(); if (auto ConditionType = getExpressionType(getCondition())) { if (not isScalarType(ConditionType)) return emitOpError() << getOperationName() << " condition requires a scalar type."; } return mlir::success(); } static clift::ExpressionRegionOpInterface getInitializerExpressionRegions(ForOp Op) { using ERI = clift::ExpressionRegionOpInterface; return clift::getOnlyOp(Op.getInitializer()); } unsigned ForOp::getExpressionRegionCount() { auto Initializer = getInitializerExpressionRegions(*this); return 2 + (Initializer ? Initializer.getExpressionRegionCount() : 0); } mlir::Region &ForOp::getExpressionRegion(unsigned Index) { if (auto Initializer = getInitializerExpressionRegions(*this)) { unsigned InitializerCount = Initializer.getExpressionRegionCount(); if (Index < InitializerCount) return Initializer.getExpressionRegion(Index); Index -= InitializerCount; } revng_assert(Index < 2); return Index == 0 ? getCondition() : getExpression(); } //===------------------------------- GotoOp -------------------------------===// MakeLabelOp GotoOp::getLabelOp() { return getLabel().getDefiningOp(); } mlir::LogicalResult GotoOp::verify() { mlir::Operation *Assignment = getLabelAssignmentOp(); if (mlir::isa(Assignment)) { if (Assignment->isAncestor(getOperation())) return emitOpError() << getOperationName() << " may not target a nesting loop label."; } return mlir::success(); } //===-------------------------------- IfOp --------------------------------===// static bool isIndirectlyNoFallthroughImpl(BranchOpInterface Branch) { for (mlir::Region &R : Branch.getBranchRegions()) { if (not clift::isIndirectlyNoFallthrough(R)) return false; } return true; } bool IfOp::isIndirectlyNoFallthrough() const { return isIndirectlyNoFallthroughImpl(*this); } mlir::LogicalResult IfOp::verify() { if (not isScalarType(getExpressionType(getCondition()))) return emitOpError() << getOperationName() << " condition requires a scalar type."; return mlir::success(); } //===--------------------------- LocalVariableOp --------------------------===// mlir::LogicalResult LocalVariableOp::verify() { if (Region &R = getInitializer(); not R.empty()) { if (getExpressionType(R) != getType().removeConst()) return emitOpError() << getOperationName() << " initializer type must match the variable type"; } return mlir::success(); } //===----------------------------- MakeLabelOp ----------------------------===// LabelAssignmentOpInterface MakeLabelOp::getAssignment() { for (mlir::OpOperand &Use : getResult().getUses()) { if (auto Op = mlir::dyn_cast(Use.getOwner())) return Op; } return {}; } static std::pair getNumLabelUsers(MakeLabelOp Op) { size_t Assignments = 0; size_t Jumps = 0; for (mlir::OpOperand &Operand : Op.getResult().getUses()) { if (mlir::isa(Operand.getOwner())) ++Assignments; else if (mlir::isa(Operand.getOwner())) ++Jumps; } return { Assignments, Jumps }; } mlir::LogicalResult MakeLabelOp::canonicalize(MakeLabelOp Op, PatternRewriter &Rewriter) { const auto [Assignments, Jumps] = getNumLabelUsers(Op); if (Jumps != 0) return mlir::failure(); if (Assignments != 0) { mlir::Operation *AssignmentOp = Op.getAssignment(); revng_assert(AssignmentOp != nullptr); if (auto AssignOp = mlir::dyn_cast(AssignmentOp)) { Rewriter.eraseOp(AssignOp); } else if (auto LoopOp = mlir::dyn_cast(AssignmentOp)) { if (Op.getResult() == LoopOp.getBreakLabel()) LoopOp.setBreakLabel(nullptr); else if (Op.getResult() == LoopOp.getContinueLabel()) LoopOp.setContinueLabel(nullptr); } } Rewriter.eraseOp(Op); return mlir::success(); } mlir::LogicalResult MakeLabelOp::verify() { const auto [Assignments, Jumps] = getNumLabelUsers(*this); if (Assignments > 1) return emitOpError() << getOperationName() << " may only have one assignment."; if (Jumps != 0 and Assignments == 0) return emitOpError() << getOperationName() << " with a use by a jump operation must have an" " assignment."; return mlir::success(); } //===------------------------------ ReturnOp ------------------------------===// mlir::LogicalResult ReturnOp::verify() { if (mlir::Region &R = getResult(); not R.empty()) { auto EmitError = [&]() -> mlir::InFlightDiagnostic { return emitOpError() << getOperationName() << " type "; }; if (verifyReturnType(EmitError, getExpressionType(R)).failed()) return mlir::failure(); } return mlir::success(); } //===------------------------------ SwitchOp ------------------------------===// bool SwitchOp::isIndirectlyNoFallthrough() const { return isIndirectlyNoFallthroughImpl(*this); } ValueType SwitchOp::getConditionType() { return getExpressionType(getConditionRegion()); } void SwitchOp::build(OpBuilder &OdsBuilder, OperationState &OdsState, const llvm::ArrayRef CaseValues) { llvm::SmallVector SignedCaseValues; SignedCaseValues.resize_for_overwrite(CaseValues.size()); std::copy(CaseValues.begin(), CaseValues.end(), SignedCaseValues.begin()); build(OdsBuilder, OdsState, SignedCaseValues, CaseValues.size()); } mlir::ParseResult SwitchOp::parse(OpAsmParser &Parser, OperationState &Result) { // Condition region: Result.addRegion(std::make_unique()); // Default case region: Result.addRegion(std::make_unique()); if (Parser.parseRegion(*Result.regions[0]).failed()) return Parser.emitError(Parser.getCurrentLocation(), "Expected switch condition region"); llvm::SmallVector CaseValues; while (Parser.parseOptionalKeyword("case").succeeded()) { uint64_t CaseValue; if (Parser.parseInteger(CaseValue).failed()) return Parser.emitError(Parser.getCurrentLocation(), "Expected switch case value"); auto R = std::make_unique(); if (Parser.parseRegion(*R).failed()) return Parser.emitError(Parser.getCurrentLocation(), "Expected switch case region"); CaseValues.push_back(static_cast(CaseValue)); Result.addRegion(std::move(R)); } if (Parser.parseOptionalKeyword("default").succeeded()) { if (Parser.parseRegion(*Result.regions[1]).failed()) return Parser.emitError(Parser.getCurrentLocation(), "Expected switch default region"); } Result.attributes.set("case_values", DenseI64ArrayAttr::get(Parser.getContext(), CaseValues)); if (Parser.parseOptionalAttrDictWithKeyword(Result.attributes).failed()) return mlir::failure(); return mlir::success(); } void SwitchOp::print(OpAsmPrinter &Printer) { Printer << ' '; Printer.printRegion(getConditionRegion()); for (unsigned I = 0, C = getNumCases(); I < C; ++I) { Printer << " case " << getCaseValue(I) << ' '; Printer.printRegion(getCaseRegion(I)); } if (hasDefaultCase()) { Printer << " default "; Printer.printRegion(getDefaultCaseRegion()); } static constexpr llvm::StringRef Elided[] = { "case_values", }; Printer.printOptionalAttrDictWithKeyword(getOperation()->getAttrs(), Elided); } mlir::LogicalResult SwitchOp::verify() { if (not isIntegerType(getExpressionType(getCondition()))) return emitOpError() << getOperationName() << " condition requires an integer type."; // One region for the condition, one for the default case and N for others. if (getNumRegions() != 2 + getCaseValues().size()) return emitOpError() << getOperationName() << " must have a case value for each case region."; llvm::SmallSet CaseValueSet; for (uint64_t const CaseValue : getCaseValues()) { if (not CaseValueSet.insert(CaseValue).second) return emitOpError() << getOperationName() << " case values must be unique."; } return mlir::success(); } //===------------------------------- WhileOp ------------------------------===// void WhileOp::build(OpBuilder &Builder, OperationState &State, LoopOpInterface OtherLoop) { buildLoop(Builder, State, 2, OtherLoop); } mlir::LogicalResult WhileOp::verify() { if (not isScalarType(getExpressionType(getCondition()))) return emitOpError() << getOperationName() << " condition requires a scalar type."; return mlir::success(); } //===----------------------------- Expressions ----------------------------===// //===------------------------------- YieldOp ------------------------------===// bool YieldOp::isDiscardedOperand(mlir::OpOperand &Operand) { mlir::Region *R = getOperation()->getParentRegion(); revng_assert(R != nullptr); auto Statement = mlir::cast(R->getParentOp()); return Statement.isDiscardedExpression(*R); } bool YieldOp::isBooleanTestedOperand(mlir::OpOperand &Operand) { mlir::Region *R = getOperation()->getParentRegion(); revng_assert(R != nullptr); auto Statement = mlir::cast(R->getParentOp()); return Statement.isBooleanTestedExpression(*R); } //===------------------------------ StringOp ------------------------------===// mlir::LogicalResult StringOp::verify() { auto ArrayT = mlir::dyn_cast(getResult().getType()); if (not ArrayT or not ArrayT.isConst()) return emitOpError() << getOperationName() << " result must have const array type."; auto CharT = mlir::dyn_cast(ArrayT.getElementType()); if (not CharT or CharT.getKind() != PrimitiveKind::NumberKind or CharT.getSize() != 1) return emitOpError() << getOperationName() << " result must have number8_t element type."; if (ArrayT.getElementsCount() != getValue().size() + 1) return emitOpError() << getOperationName() << " result type length must match string length" " (including null terminator)."; return mlir::success(); } //===----------------------- UnaryIntegerMutationOp -----------------------===// mlir::LogicalResult clift::impl::verifyUnaryIntegerMutationOp(Operation *Op) { if (not mlir::clift::isLvalueExpression(Op->getOperand(0))) return Op->emitOpError() << Op->getName() << " operand must be an lvalue-expression."; return mlir::success(); } //===------------------- Pointer arithmetic expressions -------------------===// ParseResult mlir::parseCliftPointerArithmeticOpTypes(OpAsmParser &Parser, Type &Result, Type &Lhs, Type &Rhs) { SMLoc TypesLoc = Parser.getCurrentLocation(); if (Parser.parseType(Lhs).failed()) return mlir::failure(); if (Parser.parseComma().failed()) return mlir::failure(); if (Parser.parseType(Rhs).failed()) return mlir::failure(); auto LhsPT = mlir::dyn_cast(dealias(Lhs, true)); auto RhsPT = mlir::dyn_cast(dealias(Rhs, true)); if (static_cast(LhsPT) == static_cast(RhsPT)) return Parser.emitError(TypesLoc, "Expected exactly one pointer type."); Result = clift::removeConst(LhsPT ? Lhs : Rhs); return mlir::success(); } void mlir::printCliftPointerArithmeticOpTypes(OpAsmPrinter &Printer, Operation *Op, Type Result, Type Lhs, Type Rhs) { Printer << Lhs; Printer << ','; Printer << Rhs; } static mlir::LogicalResult verifyPointerArithmeticOp(mlir::Operation *Op) { auto LhsT = mlir::cast(Op->getOperand(0).getType()); auto RhsT = mlir::cast(Op->getOperand(1).getType()); auto LhsPT = mlir::dyn_cast(dealias(LhsT, true)); auto RhsPT = mlir::dyn_cast(dealias(RhsT, true)); if (static_cast(LhsPT) == static_cast(RhsPT)) return Op->emitOpError() << "requires exactly one pointer operand."; auto PointerType = LhsPT ? LhsPT : RhsPT; auto IntegerType = mlir::dyn_cast(dealias(LhsPT ? RhsT : LhsT, true)); if (not IntegerType or not isIntegerKind(IntegerType.getKind())) return Op->emitOpError() << "requires an integer operand."; if (mlir::isa(Op)) { if (not LhsPT) return Op->emitOpError() << "left operand must have pointer type."; } if (IntegerType.getSize() != PointerType.getPointerSize()) return Op->emitOpError() << "pointer and integer operand sizes must " "match."; if (not isObjectType(PointerType.getPointeeType())) return Op->emitOpError() << "operand pointee must have object type."; if (Op->getResult(0).getType() != PointerType.removeConst()) return Op->emitOpError() << "result and pointer operand types must match."; return mlir::success(); } unsigned clift::impl::getPointerArithmeticPointerOperandIndex(mlir::Operation *Op) { return isPointerType(Op->getOperand(0).getType()) ? 0 : 1; } unsigned clift::impl::getPointerArithmeticOffsetOperandIndex(mlir::Operation *Op) { return isPointerType(Op->getOperand(0).getType()) ? 1 : 0; } //===------------------------------ PtrAddOp ------------------------------===// mlir::LogicalResult PtrAddOp::verify() { return verifyPointerArithmeticOp(getOperation()); } //===------------------------------ PtrSubOp ------------------------------===// mlir::LogicalResult PtrSubOp::verify() { return verifyPointerArithmeticOp(getOperation()); } //===------------------------------ PtrDiffOp -----------------------------===// mlir::LogicalResult PtrDiffOp::verify() { auto LhsPT = mlir::dyn_cast(dealias(getLhs().getType(), true)); auto RhsPT = mlir::dyn_cast(dealias(getRhs().getType(), true)); if (not LhsPT or not RhsPT) return emitOpError() << getOperationName() << " requires two pointer operands."; auto PointeeType = LhsPT.getPointeeType(); if (PointeeType.removeConst() != RhsPT.getPointeeType().removeConst()) return emitOpError() << getOperationName() << " operand pointee types must match, ignoring" " qualifiers."; if (not isObjectType(PointeeType)) return emitOpError() << getOperationName() << " operand pointee must have object type."; auto IntegerType = mlir::dyn_cast(getResult().getType()); if (not IntegerType or IntegerType.getKind() != PrimitiveKind::SignedKind or IntegerType.getSize() != LhsPT.getPointerSize()) return emitOpError() << getOperationName() << " result must have primitive signed integer type" " with size matching that of the operand type."; return mlir::success(); } //===------------------------------- CastOp -------------------------------===// mlir::LogicalResult CastOp::verify() { auto ResT = mlir::cast(getResult().getType()); if (ResT.isConst()) return emitOpError() << getOperationName() << " result must have unqualified type."; auto ArgT = mlir::cast(getValue().getType()); switch (auto Kind = getKind()) { case CastKind::Extend: case CastKind::Truncate: { if (auto ResUnderlyingT = getUnderlyingIntegerType(ResT)) { auto ArgUnderlyingT = getUnderlyingIntegerType(ArgT); if (not ArgUnderlyingT) return emitOpError() << " argument must have integer type."; if (ResUnderlyingT.getKind() != ArgUnderlyingT.getKind()) return emitOpError() << " result and argument types must be equal in" " kind."; } else if (auto ResPointerT = getPointerType(ResT)) { auto ArgPointerT = getPointerType(ArgT); if (not ArgPointerT) return emitOpError() << " argument must have pointer type."; if (ResPointerT.getPointeeType() != ArgPointerT.getPointeeType()) return emitOpError() << " result and argument must have equal pointee " " types."; } else { return emitOpError() << " result must have integer or pointer type."; } if (Kind == CastKind::Extend) { if (ResT.getByteSize() <= ArgT.getByteSize()) return emitOpError() << " result type must be wider than the argument" " type."; } else { if (ResT.getByteSize() >= ArgT.getByteSize()) return emitOpError() << " result type must be narrower than the" " argument type."; } } break; case CastKind::Bitcast: { if (not isObjectType(ResT) or isArrayType(ResT)) return emitOpError() << " result must have non-array object type."; if (not isObjectType(ArgT) or isArrayType(ArgT)) return emitOpError() << " argument must have non-array object type."; if (ResT.getByteSize() != ArgT.getByteSize()) return emitOpError() << " result and argument types must be equal in" " size."; } break; case CastKind::Decay: { auto PtrT = mlir::dyn_cast(ResT); if (not PtrT) return emitOpError() << getOperationName() << " result must have pointer type."; if (auto ArrayT = mlir::dyn_cast(ArgT)) { if (PtrT.getPointeeType() != ArrayT.getElementType()) return emitOpError() << getOperationName() << " the pointee type of the result type must be" " equal to the element type of the argument" " type."; } else if (auto FunctionT = mlir::dyn_cast(ArgT)) { if (PtrT.getPointeeType() != FunctionT) return emitOpError() << getOperationName() << " the pointee type of the result type must be" " equal to the argument type."; } else { return emitOpError() << getOperationName() << " argument must have array or function type."; } } break; case CastKind::Convert: { bool ArgIsFloat = isFloatType(ArgT); bool ResIsFloat = isFloatType(ResT); if (not ArgIsFloat and not isIntegerType(ArgT)) return emitOpError() << " operand must have floating point or integer" " type"; if (not ResIsFloat and not isIntegerType(ResT)) return emitOpError() << " result must have floating point or integer" " type"; if (not ArgIsFloat and not ResIsFloat) return emitOpError() << " requires either the operand or result to have" " floating point type."; if (equivalent(ArgT, ResT)) return emitOpError() << " result type cannot match the operand type."; } break; default: revng_abort("Invalid CastKind value"); } return mlir::success(); } //===----------------------------- AddressofOp ----------------------------===// mlir::LogicalResult AddressofOp::verify() { if (not clift::isLvalueExpression(getObject())) return emitOpError() << getOperationName() << " operand must be an lvalue-expression."; return mlir::success(); } //===---------------------------- IndirectionOp ---------------------------===// mlir::LogicalResult IndirectionOp::verify() { if (isVoid(getResult().getType())) return emitOpError() << getOperationName() << " cannot dereference a pointer to void."; return mlir::success(); } //===------------------------------ AssignOp ------------------------------===// mlir::LogicalResult AssignOp::verify() { if (not clift::isLvalueExpression(getLhs())) return emitOpError() << getOperationName() << " left operand must be an lvalue-expression."; return mlir::success(); } //===------------------------------ AccessOp ------------------------------===// bool AccessOp::isLvalueExpression() { return isIndirect() or clift::isLvalueExpression(getValue()); } ClassType AccessOp::getClassType() { auto ObjectT = dealias(getValue().getType(), /*IgnoreQualifiers=*/true); if (isIndirect()) { ObjectT = mlir::cast(ObjectT).getPointeeType(); ObjectT = dealias(ObjectT, /*IgnoreQualifiers=*/true); } return mlir::cast(ObjectT.removeConst()); } FieldAttr AccessOp::getFieldAttr() { return getClassType().getFields()[getMemberIndex()]; } mlir::LogicalResult AccessOp::verify() { auto ObjectT = dealias(getValue().getType()); if (auto PointerT = mlir::dyn_cast(ObjectT)) { if (not isIndirect()) return emitOpError() << getOperationName() << " operand must have pointer type."; ObjectT = dealias(PointerT.getPointeeType(), /*IgnoreQualifiers=*/true); } auto Class = mlir::dyn_cast(ObjectT); if (not Class) return emitOpError() << getOperationName() << " operand must have (pointer to) struct or union" << " type."; auto Fields = Class.getFields(); const uint64_t Index = getMemberIndex(); if (Index >= Fields.size()) return emitOpError() << getOperationName() << " struct or union member index out of range."; auto FieldT = Fields[Index].getType(); if (FieldT != getResult().getType()) return emitOpError() << getOperationName() << " result type must match the selected member type."; return mlir::success(); } //===----------------------------- SubscriptOp ----------------------------===// mlir::LogicalResult SubscriptOp::verify() { auto PointerT = mlir::dyn_cast(getPointer().getType()); if (not PointerT) return emitOpError() << getOperationName() << " operand must have pointer type."; auto PointeeT = PointerT.getPointeeType(); if (not isObjectType(PointeeT)) return emitOpError() << getOperationName() << " cannot dereference pointer to non-object type."; if (getResult().getType() != PointeeT) return emitOpError() << getOperationName() << " result type must match the pointer type."; return mlir::success(); } //===-------------------------------- UseOp -------------------------------===// mlir::LogicalResult UseOp::verifySymbolUses(SymbolTableCollection &SymbolTable) { auto Module = getOperation()->getParentOfType(); Operation *Op = SymbolTable.lookupSymbolIn(Module, getSymbolNameAttr()); if (auto V = mlir::dyn_cast_or_null(Op)) { if (getResult().getType() != V.getType()) return emitOpError() << getOperationName() << " result type must match the type of the global" " variable being referenced."; } else if (auto F = mlir::dyn_cast_or_null(Op)) { if (getResult().getType() != F.getFunctionType()) return emitOpError() << getOperationName() << " result type must match the type of the function" " being referenced."; } else { return emitOpError() << getOperationName() << " must reference a global variable or function in" " the enclosing 'builtin.module' operation."; } return mlir::success(); } //===-------------------------------- CallOp ------------------------------===// namespace { using DefaultArgumentTypeProvider = // llvm::function_ref; /// Parses an argument list delimited by parentheses with optional operand /// types. After parsing, default operand types may be provided. /// /// Syntax examples: /// (%0) /// (%0, %1) /// (%0 : !int32_t, %1) /// (%0 : !int32_t, %1 : !int32_t) class ArgumentListParser { public: ParseResult parse(OpAsmParser &Parser, bool RequireTypes) { Location = Parser.getCurrentLocation(); if (Parser.parseLParen().failed()) return mlir::failure(); if (Parser.parseOptionalRParen().failed()) { do { if (Parser.parseOperand(Operands.emplace_back()).failed()) return mlir::failure(); mlir::Type Type = {}; if (Parser.parseOptionalColon().succeeded()) { if (Parser.parseType(Type).failed()) return mlir::failure(); } else if (RequireTypes) { // Parsing an optional colon already failed, but it was actually // required. The easiest way to produce the appropriate error message // is to try parsing a non-optional colon again. return Parser.parseColon(); } Types.push_back(Type); } while (Parser.parseOptionalComma().succeeded()); if (Parser.parseRParen().failed()) return mlir::failure(); } return mlir::success(); } ParseResult resolveOperands(OpAsmParser &Parser, OperationState &Result) { return Parser.resolveOperands(Operands, Types, Location, Result.operands); } ParseResult resolveOperands(OpAsmParser &Parser, OperationState &Result, DefaultArgumentTypeProvider GetDefaultType) { for (auto [I, T] : llvm::enumerate(Types)) { if (not T) { if (clift::ValueType DefaultType = GetDefaultType(I)) T = DefaultType.removeConst(); } } return resolveOperands(Parser, Result); } private: SMLoc Location; llvm::SmallVector Operands; llvm::SmallVector Types; }; } // namespace static void printArgumentList(OpAsmPrinter &Printer, mlir::OperandRange Operands, DefaultArgumentTypeProvider GetDefaultType) { Printer << '('; for (auto [I, V] : llvm::enumerate(Operands)) { if (I != 0) Printer << ", "; Printer << V; if (clift::ValueType DefaultType = GetDefaultType(I)) if (V.getType() != DefaultType.removeConst()) Printer << " : " << V.getType(); } Printer << ')'; } static auto makeCallArgumentTypeAccessor(clift::FunctionType Function) { return [Function](unsigned I) -> clift::ValueType { auto ParameterTypes = Function.getArgumentTypes(); return I < ParameterTypes.size() ? mlir::cast(ParameterTypes[I]) : clift::ValueType(); }; } mlir::ParseResult CallOp::parse(OpAsmParser &Parser, OperationState &Result) { OpAsmParser::UnresolvedOperand FunctionOperand; if (Parser.parseOperand(FunctionOperand).failed()) return mlir::failure(); ArgumentListParser Arguments; if (Arguments.parse(Parser, /*RequireTypes=*/false).failed()) return mlir::failure(); if (Parser.parseOptionalAttrDict(Result.attributes).failed()) return mlir::failure(); if (Parser.parseColon().failed()) return mlir::failure(); mlir::SMLoc FunctionTypeLoc = Parser.getCurrentLocation(); clift::ValueType FunctionValueType; if (Parser.parseType(FunctionValueType).failed()) return mlir::failure(); auto FunctionType = getFunctionOrFunctionPointerFunctionType(FunctionValueType); if (not FunctionType) return Parser.emitError(FunctionTypeLoc) << "expected Clift function or " "pointer-to-function type"; Result.addTypes(FunctionType.getResultTypes()); if (Parser.resolveOperand(FunctionOperand, FunctionValueType, Result.operands) .failed()) return mlir::failure(); if (Arguments .resolveOperands(Parser, Result, makeCallArgumentTypeAccessor(FunctionType)) .failed()) return mlir::failure(); return mlir::success(); } void CallOp::print(OpAsmPrinter &Printer) { auto Type = getFunction().getType(); auto FunctionType = getFunctionOrFunctionPointerFunctionType(Type); revng_assert(FunctionType); // Checked by verify. Printer << ' '; Printer << getFunction(); printArgumentList(Printer, getArguments(), makeCallArgumentTypeAccessor(FunctionType)); Printer.printOptionalAttrDict(getOperation()->getAttrs(), {}); Printer << ' ' << ':' << ' ' << Type; } mlir::LogicalResult CallOp::verify() { auto FunctionValueType = mlir::cast(getFunction() .getType()); auto FunctionType = getFunctionOrFunctionPointerFunctionType(FunctionValueType); if (not FunctionType) return emitOpError() << getOperationName() << " function argument must have function or pointer" << "-to-function type."; auto ArgumentTypes = getArguments().getTypes(); auto ParameterTypes = FunctionType.getArgumentTypes(); if (ArgumentTypes.size() != ParameterTypes.size()) return emitOpError() << getOperationName() << " argument count must match the number of function" " parameters."; for (auto &&[ArgumentT, ParameterT] : llvm::zip_equal(ArgumentTypes, ParameterTypes)) { auto ArgumentValueT = mlir::cast(ArgumentT); auto ParameterValueT = mlir::cast(ParameterT); if (ArgumentValueT.removeConst() != ParameterValueT.removeConst()) return emitOpError() << getOperationName() << " argument types must match the parameter types" " of the function, ignoring qualifiers."; } auto ReturnT = mlir::cast(FunctionType.getReturnType()); auto ResultT = mlir::cast(getResult().getType()); if (ResultT != ReturnT.removeConst()) return emitOpError() << getOperationName() << " result type must match the return type of the" " function, ignoring qualifiers."; return mlir::success(); } //===------------------------------ TernaryOp -----------------------------===// ParseResult mlir::parseCliftTernaryOpTypes(OpAsmParser &Parser, Type &Condition, Type &Lhs, Type &Rhs) { if (Parser.parseType(Condition).failed()) return mlir::failure(); if (Parser.parseComma().failed()) return mlir::failure(); if (Parser.parseType(Lhs).failed()) return mlir::failure(); if (Parser.parseOptionalComma().succeeded()) { if (Parser.parseType(Rhs).failed()) return mlir::failure(); } else { Rhs = Lhs; } return mlir::success(); } void mlir::printCliftTernaryOpTypes(OpAsmPrinter &Printer, Operation *Op, Type Condition, Type Lhs, Type Rhs) { Printer << Condition; Printer << ','; Printer << Lhs; if (Lhs != Rhs) { Printer << ','; Printer << Rhs; } } //===----------------------------- AggregateOp ----------------------------===// static auto makeAggregateArgumentTypeAccessor(clift::ValueType Type) { auto UnderlyingType = dealias(Type, /*IgnoreQualifiers=*/true); return [UnderlyingType](unsigned I) -> clift::ValueType { if (auto Array = mlir::dyn_cast(UnderlyingType)) return Array.getElementType(); if (auto Struct = mlir::dyn_cast(UnderlyingType)) { auto Fields = Struct.getFields(); return I < Fields.size() ? Fields[I].getType() : clift::ValueType(); } return {}; }; } mlir::ParseResult AggregateOp::parse(OpAsmParser &Parser, OperationState &Result) { ArgumentListParser Arguments; if (Arguments.parse(Parser, /*Requiretypes=*/false).failed()) return mlir::failure(); if (Parser.parseOptionalAttrDict(Result.attributes).failed()) return mlir::failure(); if (Parser.parseColon().failed()) return mlir::failure(); clift::ValueType ResultType; if (Parser.parseType(ResultType).failed()) return mlir::failure(); if (Arguments .resolveOperands(Parser, Result, makeAggregateArgumentTypeAccessor(ResultType)) .failed()) return mlir::failure(); Result.addTypes({ ResultType }); return mlir::success(); } void AggregateOp::print(OpAsmPrinter &Printer) { clift::ValueType ResultType = getResult().getType(); printArgumentList(Printer, getInitializers(), makeAggregateArgumentTypeAccessor(ResultType)); Printer.printOptionalAttrDict(getOperation()->getAttrs(), {}); Printer << " : "; Printer << ResultType; } mlir::LogicalResult AggregateOp::verify() { auto InitializerTypes = getInitializers().getTypes(); auto AT = dealias(getResult().getType(), /*IgnoreQualifiers=*/true); if (auto T = mlir::dyn_cast(AT)) { auto Fields = T.getFields(); if (InitializerTypes.size() != Fields.size()) return emitOpError() << getOperationName() << " must initialize all struct members."; for (auto [IT, SF] : llvm::zip(InitializerTypes, Fields)) { if (not clift::equivalent(IT, SF.getType())) return emitOpError() << getOperationName() << " initializer types must match the struct field" " types."; } } else if (auto T = mlir::dyn_cast(AT)) { if (InitializerTypes.size() != T.getElementsCount()) return emitOpError() << getOperationName() << " must initialize all array elements."; for (auto IT : InitializerTypes) { if (not clift::equivalent(IT, T.getElementType())) return emitOpError() << getOperationName() << " initializer types must match the array" " element type."; } } else { return emitOpError() << getOperationName() << " result have struct or array type."; } return mlir::success(); }