| //===-- LLVMOps.td - LLVM IR dialect op definition file ----*- tablegen -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This is the LLVM IR operation definition file. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLVMIR_OPS |
| #define LLVMIR_OPS |
| |
| include "mlir/Dialect/LLVMIR/LLVMOpBase.td" |
| |
| class LLVM_Builder<string builder> { |
| string llvmBuilder = builder; |
| } |
| |
| def LLVM_OneResultOpBuilder : OpBuilder< |
| "Builder *, OperationState &result, Type resultType, " |
| "ValueRange operands, ArrayRef<NamedAttribute> attributes = {}", |
| [{ |
| if (resultType) result.addTypes(resultType); |
| result.addOperands(operands); |
| for (auto namedAttr : attributes) { |
| result.addAttribute(namedAttr.first, namedAttr.second); |
| } |
| }]>; |
| |
| def LLVM_ZeroResultOpBuilder : OpBuilder< |
| "Builder *, OperationState &result, ValueRange operands, " |
| "ArrayRef<NamedAttribute> attributes = {}", |
| [{ |
| result.addOperands(operands); |
| for (auto namedAttr : attributes) { |
| result.addAttribute(namedAttr.first, namedAttr.second); |
| } |
| }]>; |
| |
| class LLVM_TwoBuilders<OpBuilder b1, OpBuilder b2> { |
| list<OpBuilder> builders = [b1, b2]; |
| } |
| |
| // Base class for LLVM operations with one result. |
| class LLVM_OneResultOp<string mnemonic, list<OpTrait> traits = []> : |
| LLVM_Op<mnemonic, traits>, Results<(outs LLVM_Type:$res)> { |
| let builders = [LLVM_OneResultOpBuilder]; |
| } |
| |
| // Compatibility builder that takes an instance of wrapped llvm::VoidType |
| // to indicate no result. |
| def LLVM_VoidResultTypeOpBuilder : OpBuilder< |
| "Builder *builder, OperationState &result, Type resultType, " |
| "ValueRange operands, ArrayRef<NamedAttribute> attributes = {}", |
| [{ |
| auto llvmType = resultType.dyn_cast<LLVM::LLVMType>(); (void)llvmType; |
| assert(llvmType && "result must be an LLVM type"); |
| assert(llvmType.getUnderlyingType() && |
| llvmType.getUnderlyingType()->isVoidTy() && |
| "for zero-result operands, only 'void' is accepted as result type"); |
| build(builder, result, operands, attributes); |
| }]>; |
| |
| // Base class for LLVM operations with zero results. |
| class LLVM_ZeroResultOp<string mnemonic, list<OpTrait> traits = []> : |
| LLVM_Op<mnemonic, traits>, Results<(outs)>, |
| LLVM_TwoBuilders<LLVM_VoidResultTypeOpBuilder, LLVM_ZeroResultOpBuilder>; |
| |
| // Base class for LLVM terminator operations. All terminator operations have |
| // zero results and an optional list of successors. |
| class LLVM_TerminatorOp<string mnemonic, list<OpTrait> traits = []> : |
| LLVM_Op<mnemonic, !listconcat(traits, [Terminator])> { |
| let builders = [ |
| OpBuilder< |
| "Builder *, OperationState &result, " |
| "ValueRange properOperands, " |
| "ArrayRef<Block *> destinations, " |
| "ArrayRef<ValueRange> operands, " |
| "ArrayRef<NamedAttribute> attributes = {}", |
| [{ |
| result.addOperands(properOperands); |
| for (auto kvp : llvm::zip(destinations, operands)) { |
| result.addSuccessor(std::get<0>(kvp), std::get<1>(kvp)); |
| } |
| for (auto namedAttr : attributes) { |
| result.addAttribute(namedAttr.first, namedAttr.second); |
| } |
| }] |
| >, |
| OpBuilder< |
| "Builder *builder, OperationState &result, " |
| "ValueRange properOperands, " |
| "ArrayRef<Block *> destinations, " |
| "ArrayRef<NamedAttribute> attributes = {}", |
| [{ |
| SmallVector<ValueRange, 2> operands(destinations.size(), {}); |
| build(builder, result, properOperands, |
| destinations, operands, attributes); |
| }] |
| >, |
| ]; |
| } |
| |
| // Class for arithmetic binary operations. |
| class LLVM_ArithmeticOp<string mnemonic, string builderFunc, |
| list<OpTrait> traits = []> : |
| LLVM_OneResultOp<mnemonic, |
| !listconcat([NoSideEffect, SameOperandsAndResultType], traits)>, |
| Arguments<(ins LLVM_Type:$lhs, LLVM_Type:$rhs)>, |
| LLVM_Builder<"$res = builder." # builderFunc # "($lhs, $rhs);"> { |
| let parser = [{ return impl::parseOneResultSameOperandTypeOp(parser, result); }]; |
| let printer = [{ mlir::impl::printOneResultOp(this->getOperation(), p); }]; |
| } |
| class LLVM_UnaryArithmeticOp<string mnemonic, string builderFunc, |
| list<OpTrait> traits = []> : |
| LLVM_OneResultOp<mnemonic, |
| !listconcat([NoSideEffect, SameOperandsAndResultType], traits)>, |
| Arguments<(ins LLVM_Type:$operand)>, |
| LLVM_Builder<"$res = builder." # builderFunc # "($operand);"> { |
| let parser = [{ return impl::parseOneResultSameOperandTypeOp(parser, result); }]; |
| let printer = [{ mlir::impl::printOneResultOp(this->getOperation(), p); }]; |
| } |
| |
| // Integer binary operations. |
| def LLVM_AddOp : LLVM_ArithmeticOp<"add", "CreateAdd", [Commutative]>; |
| def LLVM_SubOp : LLVM_ArithmeticOp<"sub", "CreateSub">; |
| def LLVM_MulOp : LLVM_ArithmeticOp<"mul", "CreateMul", [Commutative]>; |
| def LLVM_UDivOp : LLVM_ArithmeticOp<"udiv", "CreateUDiv">; |
| def LLVM_SDivOp : LLVM_ArithmeticOp<"sdiv", "CreateSDiv">; |
| def LLVM_URemOp : LLVM_ArithmeticOp<"urem", "CreateURem">; |
| def LLVM_SRemOp : LLVM_ArithmeticOp<"srem", "CreateSRem">; |
| def LLVM_AndOp : LLVM_ArithmeticOp<"and", "CreateAnd">; |
| def LLVM_OrOp : LLVM_ArithmeticOp<"or", "CreateOr">; |
| def LLVM_XOrOp : LLVM_ArithmeticOp<"xor", "CreateXor">; |
| def LLVM_ShlOp : LLVM_ArithmeticOp<"shl", "CreateShl">; |
| def LLVM_LShrOp : LLVM_ArithmeticOp<"lshr", "CreateLShr">; |
| def LLVM_AShrOp : LLVM_ArithmeticOp<"ashr", "CreateAShr">; |
| |
| // Predicate for integer comparisons. |
| def ICmpPredicateEQ : I64EnumAttrCase<"eq", 0>; |
| def ICmpPredicateNE : I64EnumAttrCase<"ne", 1>; |
| def ICmpPredicateSLT : I64EnumAttrCase<"slt", 2>; |
| def ICmpPredicateSLE : I64EnumAttrCase<"sle", 3>; |
| def ICmpPredicateSGT : I64EnumAttrCase<"sgt", 4>; |
| def ICmpPredicateSGE : I64EnumAttrCase<"sge", 5>; |
| def ICmpPredicateULT : I64EnumAttrCase<"ult", 6>; |
| def ICmpPredicateULE : I64EnumAttrCase<"ule", 7>; |
| def ICmpPredicateUGT : I64EnumAttrCase<"ugt", 8>; |
| def ICmpPredicateUGE : I64EnumAttrCase<"uge", 9>; |
| def ICmpPredicate : I64EnumAttr< |
| "ICmpPredicate", |
| "llvm.icmp comparison predicate", |
| [ICmpPredicateEQ, ICmpPredicateNE, ICmpPredicateSLT, ICmpPredicateSLE, |
| ICmpPredicateSGT, ICmpPredicateSGE, ICmpPredicateULT, ICmpPredicateULE, |
| ICmpPredicateUGT, ICmpPredicateUGE]> { |
| let cppNamespace = "::mlir::LLVM"; |
| } |
| |
| // Other integer operations. |
| def LLVM_ICmpOp : LLVM_OneResultOp<"icmp", [NoSideEffect]>, |
| Arguments<(ins ICmpPredicate:$predicate, LLVM_Type:$lhs, |
| LLVM_Type:$rhs)> { |
| let llvmBuilder = [{ |
| $res = builder.CreateICmp(getLLVMCmpPredicate($predicate), $lhs, $rhs); |
| }]; |
| let builders = [OpBuilder< |
| "Builder *b, OperationState &result, ICmpPredicate predicate, Value lhs, " |
| "Value rhs", [{ |
| LLVMDialect *dialect = &lhs.getType().cast<LLVMType>().getDialect(); |
| build(b, result, LLVMType::getInt1Ty(dialect), |
| b->getI64IntegerAttr(static_cast<int64_t>(predicate)), lhs, rhs); |
| }]>]; |
| let parser = [{ return parseCmpOp<ICmpPredicate>(parser, result); }]; |
| let printer = [{ printICmpOp(p, *this); }]; |
| } |
| |
| // Predicate for float comparisons |
| def FCmpPredicateFALSE : I64EnumAttrCase<"_false", 0>; |
| def FCmpPredicateOEQ : I64EnumAttrCase<"oeq", 1>; |
| def FCmpPredicateOGT : I64EnumAttrCase<"ogt", 2>; |
| def FCmpPredicateOGE : I64EnumAttrCase<"oge", 3>; |
| def FCmpPredicateOLT : I64EnumAttrCase<"olt", 4>; |
| def FCmpPredicateOLE : I64EnumAttrCase<"ole", 5>; |
| def FCmpPredicateONE : I64EnumAttrCase<"one", 6>; |
| def FCmpPredicateORD : I64EnumAttrCase<"ord", 7>; |
| def FCmpPredicateUEQ : I64EnumAttrCase<"ueq", 8>; |
| def FCmpPredicateUGT : I64EnumAttrCase<"ugt", 9>; |
| def FCmpPredicateUGE : I64EnumAttrCase<"uge", 10>; |
| def FCmpPredicateULT : I64EnumAttrCase<"ult", 11>; |
| def FCmpPredicateULE : I64EnumAttrCase<"ule", 12>; |
| def FCmpPredicateUNE : I64EnumAttrCase<"une", 13>; |
| def FCmpPredicateUNO : I64EnumAttrCase<"uno", 14>; |
| def FCmpPredicateTRUE : I64EnumAttrCase<"_true", 15>; |
| |
| def FCmpPredicate : I64EnumAttr< |
| "FCmpPredicate", |
| "llvm.fcmp comparison predicate", |
| [FCmpPredicateFALSE, FCmpPredicateOEQ, FCmpPredicateOGT, FCmpPredicateOGE, |
| FCmpPredicateOLT, FCmpPredicateOLE, FCmpPredicateONE, FCmpPredicateORD, |
| FCmpPredicateUEQ, FCmpPredicateUGT, FCmpPredicateUGE, FCmpPredicateULT, |
| FCmpPredicateULE, FCmpPredicateUNE, FCmpPredicateUNO, FCmpPredicateTRUE |
| ]> { |
| let cppNamespace = "::mlir::LLVM"; |
| } |
| |
| // Other integer operations. |
| def LLVM_FCmpOp : LLVM_OneResultOp<"fcmp", [NoSideEffect]>, |
| Arguments<(ins FCmpPredicate:$predicate, LLVM_Type:$lhs, |
| LLVM_Type:$rhs)> { |
| let llvmBuilder = [{ |
| $res = builder.CreateFCmp(getLLVMCmpPredicate($predicate), $lhs, $rhs); |
| }]; |
| let builders = [OpBuilder< |
| "Builder *b, OperationState &result, FCmpPredicate predicate, Value lhs, " |
| "Value rhs", [{ |
| LLVMDialect *dialect = &lhs.getType().cast<LLVMType>().getDialect(); |
| build(b, result, LLVMType::getInt1Ty(dialect), |
| b->getI64IntegerAttr(static_cast<int64_t>(predicate)), lhs, rhs); |
| }]>]; |
| let parser = [{ return parseCmpOp<FCmpPredicate>(parser, result); }]; |
| let printer = [{ printFCmpOp(p, *this); }]; |
| } |
| |
| // Floating point binary operations. |
| def LLVM_FAddOp : LLVM_ArithmeticOp<"fadd", "CreateFAdd">; |
| def LLVM_FSubOp : LLVM_ArithmeticOp<"fsub", "CreateFSub">; |
| def LLVM_FMulOp : LLVM_ArithmeticOp<"fmul", "CreateFMul">; |
| def LLVM_FDivOp : LLVM_ArithmeticOp<"fdiv", "CreateFDiv">; |
| def LLVM_FRemOp : LLVM_ArithmeticOp<"frem", "CreateFRem">; |
| def LLVM_FNegOp : LLVM_UnaryArithmeticOp<"fneg", "CreateFNeg">; |
| |
| // Memory-related operations. |
| def LLVM_AllocaOp : |
| LLVM_OneResultOp<"alloca">, |
| Arguments<(ins LLVM_Type:$arraySize, OptionalAttr<I64Attr>:$alignment)> { |
| string llvmBuilder = [{ |
| auto *alloca = builder.CreateAlloca( |
| $_resultType->getPointerElementType(), $arraySize); |
| if ($alignment.hasValue()) { |
| auto align = $alignment.getValue().getZExtValue(); |
| if (align != 0) |
| alloca->setAlignment(llvm::MaybeAlign(align)); |
| } |
| $res = alloca; |
| }]; |
| let builders = [OpBuilder< |
| "Builder *b, OperationState &result, Type resultType, Value arraySize, " |
| "unsigned alignment", |
| [{ |
| if (alignment == 0) |
| return build(b, result, resultType, arraySize, IntegerAttr()); |
| build(b, result, resultType, arraySize, b->getI64IntegerAttr(alignment)); |
| }]>]; |
| let parser = [{ return parseAllocaOp(parser, result); }]; |
| let printer = [{ printAllocaOp(p, *this); }]; |
| let verifier = [{ |
| if (alignment().hasValue()) { |
| auto align = alignment().getValue().getSExtValue(); |
| if (align < 0) |
| return emitOpError("expected positive alignment"); |
| } |
| return success(); |
| }]; |
| } |
| def LLVM_GEPOp : LLVM_OneResultOp<"getelementptr", [NoSideEffect]>, |
| Arguments<(ins LLVM_Type:$base, Variadic<LLVM_Type>:$indices)>, |
| LLVM_Builder<"$res = builder.CreateGEP($base, $indices);"> { |
| let assemblyFormat = [{ |
| $base `[` $indices `]` attr-dict `:` functional-type(operands, results) |
| }]; |
| } |
| def LLVM_LoadOp : LLVM_OneResultOp<"load">, Arguments<(ins LLVM_Type:$addr)>, |
| LLVM_Builder<"$res = builder.CreateLoad($addr);"> { |
| let builders = [OpBuilder< |
| "Builder *b, OperationState &result, Value addr", |
| [{ |
| auto type = addr.getType().cast<LLVM::LLVMType>().getPointerElementTy(); |
| build(b, result, type, addr); |
| }]>]; |
| let parser = [{ return parseLoadOp(parser, result); }]; |
| let printer = [{ printLoadOp(p, *this); }]; |
| } |
| def LLVM_StoreOp : LLVM_ZeroResultOp<"store">, |
| Arguments<(ins LLVM_Type:$value, LLVM_Type:$addr)>, |
| LLVM_Builder<"builder.CreateStore($value, $addr);"> { |
| let parser = [{ return parseStoreOp(parser, result); }]; |
| let printer = [{ printStoreOp(p, *this); }]; |
| } |
| |
| // Casts. |
| class LLVM_CastOp<string mnemonic, string builderFunc, |
| list<OpTrait> traits = []> : |
| LLVM_OneResultOp<mnemonic, |
| !listconcat([NoSideEffect], traits)>, |
| Arguments<(ins LLVM_Type:$arg)>, |
| LLVM_Builder<"$res = builder." # builderFunc # "($arg, $_resultType);"> { |
| let parser = [{ return mlir::impl::parseCastOp(parser, result); }]; |
| let printer = [{ mlir::impl::printCastOp(this->getOperation(), p); }]; |
| } |
| def LLVM_BitcastOp : LLVM_CastOp<"bitcast", "CreateBitCast">; |
| def LLVM_AddrSpaceCastOp : LLVM_CastOp<"addrspacecast", "CreateAddrSpaceCast">; |
| def LLVM_IntToPtrOp : LLVM_CastOp<"inttoptr", "CreateIntToPtr">; |
| def LLVM_PtrToIntOp : LLVM_CastOp<"ptrtoint", "CreatePtrToInt">; |
| def LLVM_SExtOp : LLVM_CastOp<"sext", "CreateSExt">; |
| def LLVM_ZExtOp : LLVM_CastOp<"zext", "CreateZExt">; |
| def LLVM_TruncOp : LLVM_CastOp<"trunc", "CreateTrunc">; |
| def LLVM_SIToFPOp : LLVM_CastOp<"sitofp", "CreateSIToFP">; |
| def LLVM_UIToFPOp : LLVM_CastOp<"uitofp", "CreateUIToFP">; |
| def LLVM_FPToSIOp : LLVM_CastOp<"fptosi", "CreateFPToSI">; |
| def LLVM_FPToUIOp : LLVM_CastOp<"fptoui", "CreateFPToUI">; |
| def LLVM_FPExtOp : LLVM_CastOp<"fpext", "CreateFPExt">; |
| def LLVM_FPTruncOp : LLVM_CastOp<"fptrunc", "CreateFPTrunc">; |
| |
| // Call-related operations. |
| def LLVM_InvokeOp : LLVM_Op<"invoke", [Terminator]>, |
| Arguments<(ins OptionalAttr<FlatSymbolRefAttr>:$callee, |
| Variadic<LLVM_Type>)>, |
| Results<(outs Variadic<LLVM_Type>)> { |
| let successors = (successor AnySuccessor:$normalDest, |
| AnySuccessor:$unwindDest); |
| |
| let builders = [OpBuilder< |
| "Builder *b, OperationState &result, ArrayRef<Type> tys, " |
| "ValueRange ops, Block* normal, " |
| "ValueRange normalOps, Block* unwind, ValueRange unwindOps", |
| [{ |
| result.addTypes(tys); |
| result.addOperands(ops); |
| result.addSuccessor(normal, normalOps); |
| result.addSuccessor(unwind, unwindOps); |
| }]>]; |
| let verifier = [{ return ::verify(*this); }]; |
| let parser = [{ return parseInvokeOp(parser, result); }]; |
| let printer = [{ printInvokeOp(p, *this); }]; |
| } |
| |
| def LLVM_LandingpadOp : LLVM_OneResultOp<"landingpad">, |
| Arguments<(ins UnitAttr:$cleanup, |
| Variadic<LLVM_Type>)> { |
| let verifier = [{ return ::verify(*this); }]; |
| let parser = [{ return parseLandingpadOp(parser, result); }]; |
| let printer = [{ printLandingpadOp(p, *this); }]; |
| } |
| |
| def LLVM_CallOp : LLVM_Op<"call">, |
| Arguments<(ins OptionalAttr<FlatSymbolRefAttr>:$callee, |
| Variadic<LLVM_Type>)>, |
| Results<(outs Variadic<LLVM_Type>)> { |
| let builders = [OpBuilder< |
| "Builder *builder, OperationState &result, LLVMFuncOp func," |
| "ValueRange operands, ArrayRef<NamedAttribute> attributes = {}", |
| [{ |
| LLVMType resultType = func.getType().getFunctionResultType(); |
| if (!resultType.isVoidTy()) |
| result.addTypes(resultType); |
| result.addAttribute("callee", builder->getSymbolRefAttr(func)); |
| result.addAttributes(attributes); |
| result.addOperands(operands); |
| }]>]; |
| let verifier = [{ |
| if (getNumResults() > 1) |
| return emitOpError("must have 0 or 1 result"); |
| return success(); |
| }]; |
| let parser = [{ return parseCallOp(parser, result); }]; |
| let printer = [{ printCallOp(p, *this); }]; |
| } |
| def LLVM_ExtractElementOp : LLVM_OneResultOp<"extractelement", [NoSideEffect]>, |
| Arguments<(ins LLVM_Type:$vector, |
| LLVM_Type:$position)> { |
| string llvmBuilder = [{ |
| $res = builder.CreateExtractElement($vector, $position); |
| }]; |
| let builders = [OpBuilder< |
| "Builder *b, OperationState &result, Value vector, Value position," |
| "ArrayRef<NamedAttribute> attrs = {}">]; |
| let parser = [{ return parseExtractElementOp(parser, result); }]; |
| let printer = [{ printExtractElementOp(p, *this); }]; |
| } |
| def LLVM_ExtractValueOp : LLVM_OneResultOp<"extractvalue", [NoSideEffect]>, |
| Arguments<(ins LLVM_Type:$container, |
| ArrayAttr:$position)> { |
| string llvmBuilder = [{ |
| $res = builder.CreateExtractValue($container, extractPosition($position)); |
| }]; |
| let parser = [{ return parseExtractValueOp(parser, result); }]; |
| let printer = [{ printExtractValueOp(p, *this); }]; |
| } |
| def LLVM_InsertElementOp : LLVM_OneResultOp<"insertelement", [NoSideEffect]>, |
| Arguments<(ins LLVM_Type:$vector, LLVM_Type:$value, |
| LLVM_Type:$position)> { |
| string llvmBuilder = [{ |
| $res = builder.CreateInsertElement($vector, $value, $position); |
| }]; |
| let parser = [{ return parseInsertElementOp(parser, result); }]; |
| let printer = [{ printInsertElementOp(p, *this); }]; |
| } |
| def LLVM_InsertValueOp : LLVM_OneResultOp<"insertvalue", [NoSideEffect]>, |
| Arguments<(ins LLVM_Type:$container, LLVM_Type:$value, |
| ArrayAttr:$position)> { |
| string llvmBuilder = [{ |
| $res = builder.CreateInsertValue($container, $value, |
| extractPosition($position)); |
| }]; |
| let builders = [OpBuilder< |
| "Builder *b, OperationState &result, Value container, Value value, " |
| "ArrayAttr position", |
| [{ |
| build(b, result, container.getType(), container, value, position); |
| }]>]; |
| let parser = [{ return parseInsertValueOp(parser, result); }]; |
| let printer = [{ printInsertValueOp(p, *this); }]; |
| } |
| def LLVM_ShuffleVectorOp |
| : LLVM_OneResultOp<"shufflevector", [NoSideEffect]>, |
| Arguments<(ins LLVM_Type:$v1, LLVM_Type:$v2, ArrayAttr:$mask)>, |
| LLVM_Builder< |
| "$res = builder.CreateShuffleVector($v1, $v2, extractPosition($mask));"> { |
| let builders = [OpBuilder< |
| "Builder *b, OperationState &result, Value v1, Value v2, " |
| "ArrayAttr mask, ArrayRef<NamedAttribute> attrs = {}">]; |
| let verifier = [{ |
| auto wrappedVectorType1 = v1().getType().cast<LLVM::LLVMType>(); |
| auto wrappedVectorType2 = v2().getType().cast<LLVM::LLVMType>(); |
| if (!wrappedVectorType2.getUnderlyingType()->isVectorTy()) |
| return emitOpError("expected LLVM IR Dialect vector type for operand #2"); |
| if (wrappedVectorType1.getVectorElementType() != |
| wrappedVectorType2.getVectorElementType()) |
| return emitOpError("expected matching LLVM IR Dialect element types"); |
| return success(); |
| }]; |
| let parser = [{ return parseShuffleVectorOp(parser, result); }]; |
| let printer = [{ printShuffleVectorOp(p, *this); }]; |
| } |
| |
| // Misc operations. |
| def LLVM_SelectOp |
| : LLVM_OneResultOp<"select", |
| [NoSideEffect, AllTypesMatch<["trueValue", "falseValue", "res"]>]>, |
| Arguments<(ins LLVM_Type:$condition, LLVM_Type:$trueValue, |
| LLVM_Type:$falseValue)>, |
| LLVM_Builder< |
| "$res = builder.CreateSelect($condition, $trueValue, $falseValue);"> { |
| let builders = [OpBuilder< |
| "Builder *b, OperationState &result, Value condition, Value lhs, " |
| "Value rhs", [{ |
| build(b, result, lhs.getType(), condition, lhs, rhs); |
| }]>]; |
| let assemblyFormat = "operands attr-dict `:` type($condition) `,` type($res)"; |
| } |
| |
| // Terminators. |
| def LLVM_BrOp : LLVM_TerminatorOp<"br", []> { |
| let successors = (successor AnySuccessor:$dest); |
| let parser = [{ return parseBrOp(parser, result); }]; |
| let printer = [{ printBrOp(p, *this); }]; |
| } |
| def LLVM_CondBrOp : LLVM_TerminatorOp<"cond_br", []> { |
| let arguments = (ins LLVMI1:$condition); |
| let successors = (successor AnySuccessor:$trueDest, AnySuccessor:$falseDest); |
| |
| let parser = [{ return parseCondBrOp(parser, result); }]; |
| let printer = [{ printCondBrOp(p, *this); }]; |
| } |
| def LLVM_ReturnOp : LLVM_TerminatorOp<"return", []>, |
| Arguments<(ins Variadic<LLVM_Type>:$args)> { |
| string llvmBuilder = [{ |
| if ($_numOperands != 0) |
| builder.CreateRet($args[0]); |
| else |
| builder.CreateRetVoid(); |
| }]; |
| |
| let verifier = [{ |
| if (getNumOperands() > 1) |
| return emitOpError("expects at most 1 operand"); |
| return success(); |
| }]; |
| |
| let parser = [{ return parseReturnOp(parser, result); }]; |
| let printer = [{ printReturnOp(p, *this); }]; |
| } |
| def LLVM_UnreachableOp : LLVM_TerminatorOp<"unreachable", []> { |
| string llvmBuilder = [{ builder.CreateUnreachable(); }]; |
| let parser = [{ return success(); }]; |
| let printer = [{ p << getOperationName(); }]; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Auxiliary operations (do not appear in LLVM IR but necessary for the dialect |
| // to work correctly). |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| // Linkage attribute is used on functions and globals. The order follows that of |
| // https://llvm.org/docs/LangRef.html#linkage-types. The names are equivalent to |
| // visible names in the IR rather than to enum values names in llvm::GlobalValue |
| // since the latter is easier to change. |
| def LinkagePrivate |
| : LLVM_EnumAttrCase<"Private", "private", "PrivateLinkage", 0>; |
| def LinkageInternal |
| : LLVM_EnumAttrCase<"Internal", "internal", "InternalLinkage", 1>; |
| def LinkageAvailableExternally |
| : LLVM_EnumAttrCase<"AvailableExternally", "available_externally", |
| "AvailableExternallyLinkage", 2>; |
| def LinkageLinkonce |
| : LLVM_EnumAttrCase<"Linkonce", "linkonce", "LinkOnceAnyLinkage", 3>; |
| def LinkageWeak |
| : LLVM_EnumAttrCase<"Weak", "weak", "WeakAnyLinkage", 4>; |
| def LinkageCommon |
| : LLVM_EnumAttrCase<"Common", "common", "CommonLinkage", 5>; |
| def LinkageAppending |
| : LLVM_EnumAttrCase<"Appending", "appending", "AppendingLinkage", 6>; |
| def LinkageExternWeak |
| : LLVM_EnumAttrCase<"ExternWeak", "extern_weak", "ExternalWeakLinkage", 7>; |
| def LinkageLinkonceODR |
| : LLVM_EnumAttrCase<"LinkonceODR", "linkonce_odr", "LinkOnceODRLinkage", 8>; |
| def LinkageWeakODR |
| : LLVM_EnumAttrCase<"WeakODR", "weak_odr", "WeakODRLinkage", 9>; |
| def LinkageExternal |
| : LLVM_EnumAttrCase<"External", "external", "ExternalLinkage", 10>; |
| |
| def Linkage : LLVM_EnumAttr< |
| "Linkage", |
| "::llvm::GlobalValue::LinkageTypes", |
| "LLVM linkage types", |
| [LinkagePrivate, LinkageInternal, LinkageAvailableExternally, |
| LinkageLinkonce, LinkageWeak, LinkageCommon, LinkageAppending, |
| LinkageExternWeak, LinkageLinkonceODR, LinkageWeakODR, LinkageExternal]> { |
| let cppNamespace = "::mlir::LLVM"; |
| } |
| |
| |
| def LLVM_AddressOfOp |
| : LLVM_OneResultOp<"mlir.addressof">, |
| Arguments<(ins FlatSymbolRefAttr:$global_name)> { |
| let builders = [ |
| OpBuilder<"Builder *builder, OperationState &result, LLVMType resType, " |
| "StringRef name, ArrayRef<NamedAttribute> attrs = {}", [{ |
| result.addAttribute("global_name", builder->getSymbolRefAttr(name)); |
| result.addAttributes(attrs); |
| result.addTypes(resType);}]>, |
| |
| OpBuilder<"Builder *builder, OperationState &result, GlobalOp global, " |
| "ArrayRef<NamedAttribute> attrs = {}", [{ |
| build(builder, result, |
| global.getType().getPointerTo(global.addr_space().getZExtValue()), |
| global.sym_name(), attrs);}]> |
| ]; |
| |
| let extraClassDeclaration = [{ |
| /// Return the llvm.mlir.global operation that defined the value referenced |
| /// here. |
| GlobalOp getGlobal(); |
| }]; |
| |
| let assemblyFormat = "$global_name attr-dict `:` type($res)"; |
| let verifier = "return ::verify(*this);"; |
| } |
| |
| def LLVM_GlobalOp |
| : LLVM_ZeroResultOp<"mlir.global", |
| [IsolatedFromAbove, |
| SingleBlockImplicitTerminator<"ReturnOp">, Symbol]>, |
| Arguments<(ins TypeAttr:$type, UnitAttr:$constant, StrAttr:$sym_name, |
| Linkage:$linkage, |
| OptionalAttr<AnyAttr>:$value, |
| DefaultValuedAttr<NonNegativeI32Attr, "0">:$addr_space)> { |
| let summary = "LLVM dialect global."; |
| let description = [{ |
| Can contain an optional initializer region or attribute for simple |
| initializers. |
| |
| Examples: |
| // Initialized using an attribute. |
| llvm.mlir.global @a("abc") : !llvm<"[3 x i8]"> |
| // Initialized using a region. |
| llvm.mlir.global constant @b() : !llvm<"i32*"> { |
| %0 = llvm.constant(0 : i32) : !llvm.i32 |
| %1 = llvm.inttoptr %0 : !llvm.i32 to !llvm<"i32*"> |
| llvm.return %1 : !llvm<"i32*"> |
| } |
| }]; |
| let regions = (region AnyRegion:$initializer); |
| |
| let builders = [ |
| OpBuilder<"Builder *builder, OperationState &result, LLVMType type, " |
| "bool isConstant, Linkage linkage, StringRef name, " |
| "Attribute value, unsigned addrSpace = 0, " |
| "ArrayRef<NamedAttribute> attrs = {}"> |
| ]; |
| |
| let extraClassDeclaration = [{ |
| /// Return the LLVM type of the global. |
| LLVMType getType() { |
| return type().cast<LLVMType>(); |
| } |
| /// Return the initializer attribute if it exists, or a null attribute. |
| Attribute getValueOrNull() { |
| return value().getValueOr(Attribute()); |
| } |
| /// Return the initializer region. This may be empty, but if it is not it |
| /// terminates in an `llvm.return` op with the initializer value. |
| Region &getInitializerRegion() { |
| return getOperation()->getRegion(0); |
| } |
| /// Return the initializer block. If the initializer region is empty this |
| /// is nullptr. If it is not nullptr, it terminates with an `llvm.return` |
| /// op with the initializer value. |
| Block *getInitializerBlock() { |
| return getInitializerRegion().empty() ? |
| nullptr : &getInitializerRegion().front(); |
| } |
| }]; |
| |
| let printer = "printGlobalOp(p, *this);"; |
| let parser = "return parseGlobalOp(parser, result);"; |
| let verifier = "return ::verify(*this);"; |
| } |
| |
| def LLVM_LLVMFuncOp |
| : LLVM_ZeroResultOp<"func", [IsolatedFromAbove, FunctionLike, Symbol]>, |
| Arguments<(ins DefaultValuedAttr<Linkage, |
| "Linkage::External">:$linkage)> { |
| let summary = "LLVM dialect function, has wrapped LLVM IR function type"; |
| |
| let regions = (region AnyRegion:$body); |
| |
| let skipDefaultBuilders = 1; |
| |
| let builders = [ |
| OpBuilder<"Builder *builder, OperationState &result, StringRef name, " |
| "LLVMType type, LLVM::Linkage linkage = LLVM::Linkage::External, " |
| "ArrayRef<NamedAttribute> attrs = {}, " |
| "ArrayRef<NamedAttributeList> argAttrs = {}"> |
| ]; |
| |
| let extraClassDeclaration = [{ |
| // Add an entry block to an empty function, and set up the block arguments |
| // to match the signature of the function. |
| Block *addEntryBlock(); |
| |
| LLVMType getType() { |
| return getAttrOfType<TypeAttr>(getTypeAttrName()) |
| .getValue().cast<LLVMType>(); |
| } |
| bool isVarArg() { |
| return getType().getUnderlyingType()->isFunctionVarArg(); |
| } |
| |
| // Hook for OpTrait::FunctionLike, returns the number of function arguments. |
| // Depends on the type attribute being correct as checked by verifyType. |
| unsigned getNumFuncArguments(); |
| |
| // Hook for OpTrait::FunctionLike, returns the number of function results. |
| // Depends on the type attribute being correct as checked by verifyType. |
| unsigned getNumFuncResults(); |
| |
| // Hook for OpTrait::FunctionLike, called after verifying that the 'type' |
| // attribute is present. This can check for preconditions of the |
| // getNumArguments hook not failing. |
| LogicalResult verifyType(); |
| }]; |
| |
| let verifier = [{ return ::verify(*this); }]; |
| let printer = [{ printLLVMFuncOp(p, *this); }]; |
| let parser = [{ return parseLLVMFuncOp(parser, result); }]; |
| } |
| |
| def LLVM_NullOp |
| : LLVM_OneResultOp<"mlir.null", [NoSideEffect]>, |
| LLVM_Builder<"$res = llvm::ConstantPointerNull::get(" |
| " cast<llvm::PointerType>($_resultType));"> { |
| let assemblyFormat = "attr-dict `:` type($res)"; |
| let verifier = [{ return ::verify(*this); }]; |
| } |
| |
| def LLVM_UndefOp : LLVM_OneResultOp<"mlir.undef", [NoSideEffect]>, |
| LLVM_Builder<"$res = llvm::UndefValue::get($_resultType);"> { |
| let assemblyFormat = "attr-dict `:` type($res)"; |
| } |
| def LLVM_ConstantOp |
| : LLVM_OneResultOp<"mlir.constant", [NoSideEffect]>, |
| Arguments<(ins AnyAttr:$value)>, |
| LLVM_Builder<"$res = getLLVMConstant($_resultType, $value, $_location);"> |
| { |
| let assemblyFormat = "`(` $value `)` attr-dict `:` type($res)"; |
| } |
| |
| // Operations that correspond to LLVM intrinsics. With MLIR operation set being |
| // extendable, there is no reason to introduce a hard boundary between "core" |
| // operations and intrinsics. However, we systematically prefix them with |
| // "intr." to avoid potential name clashes. |
| |
| class LLVM_UnaryIntrinsicOp<string func, list<OpTrait> traits = []> : |
| LLVM_OneResultOp<"intr." # func, |
| !listconcat([NoSideEffect, SameOperandsAndResultType], traits)>, |
| Arguments<(ins LLVM_Type:$in)>, |
| LLVM_Builder<"$res = builder.CreateCall(llvm::Intrinsic::getDeclaration(" |
| "builder.GetInsertBlock()->getModule(), llvm::Intrinsic::" # func # "," |
| "{$in->getType()}), {$in});"> { |
| } |
| |
| class LLVM_BinarySameArgsIntrinsicOp<string func, list<OpTrait> traits = []> : |
| LLVM_OneResultOp<"intr." # func, |
| !listconcat([NoSideEffect, SameOperandsAndResultType], traits)>, |
| Arguments<(ins LLVM_Type:$a, LLVM_Type:$b)>, |
| LLVM_Builder<"$res = builder.CreateCall(llvm::Intrinsic::getDeclaration(" |
| "builder.GetInsertBlock()->getModule(), llvm::Intrinsic::" # func # "," |
| "{$a->getType()}), {$a, $b});"> { |
| } |
| |
| class LLVM_TernarySameArgsIntrinsicOp<string func, list<OpTrait> traits = []> : |
| LLVM_OneResultOp<"intr." # func, |
| !listconcat([NoSideEffect, SameOperandsAndResultType], traits)>, |
| Arguments<(ins LLVM_Type:$a, LLVM_Type:$b, LLVM_Type:$c)>, |
| LLVM_Builder<"$res = builder.CreateCall(llvm::Intrinsic::getDeclaration(" |
| "builder.GetInsertBlock()->getModule(), llvm::Intrinsic::" # func # "," |
| "{$a->getType()}), {$a, $b, $c});"> { |
| } |
| |
| def LLVM_ExpOp : LLVM_UnaryIntrinsicOp<"exp">; |
| def LLVM_FAbsOp : LLVM_UnaryIntrinsicOp<"fabs">; |
| def LLVM_FCeilOp : LLVM_UnaryIntrinsicOp<"ceil">; |
| def LLVM_CosOp : LLVM_UnaryIntrinsicOp<"cos">; |
| def LLVM_CopySignOp : LLVM_BinarySameArgsIntrinsicOp<"copysign">; |
| def LLVM_FMAOp : LLVM_TernarySameArgsIntrinsicOp<"fma">; |
| def LLVM_FMulAddOp : LLVM_TernarySameArgsIntrinsicOp<"fmuladd">; |
| def LLVM_SqrtOp : LLVM_UnaryIntrinsicOp<"sqrt">; |
| |
| def LLVM_LogOp : LLVM_Op<"intr.log", [NoSideEffect]>, |
| Arguments<(ins LLVM_Type:$in)>, |
| Results<(outs LLVM_Type:$res)> { |
| let llvmBuilder = [{ |
| llvm::Module *module = builder.GetInsertBlock()->getModule(); |
| llvm::Function *fn = llvm::Intrinsic::getDeclaration( |
| module, llvm::Intrinsic::log, {$in->getType()}); |
| $res = builder.CreateCall(fn, {$in}); |
| }]; |
| } |
| |
| def LLVM_Log10Op : LLVM_Op<"intr.log10", [NoSideEffect]>, |
| Arguments<(ins LLVM_Type:$in)>, |
| Results<(outs LLVM_Type:$res)> { |
| let llvmBuilder = [{ |
| llvm::Module *module = builder.GetInsertBlock()->getModule(); |
| llvm::Function *fn = llvm::Intrinsic::getDeclaration( |
| module, llvm::Intrinsic::log10, {$in->getType()}); |
| $res = builder.CreateCall(fn, {$in}); |
| }]; |
| } |
| |
| def LLVM_Log2Op : LLVM_Op<"intr.log2", [NoSideEffect]>, |
| Arguments<(ins LLVM_Type:$in)>, |
| Results<(outs LLVM_Type:$res)> { |
| let llvmBuilder = [{ |
| llvm::Module *module = builder.GetInsertBlock()->getModule(); |
| llvm::Function *fn = llvm::Intrinsic::getDeclaration( |
| module, llvm::Intrinsic::log2, {$in->getType()}); |
| $res = builder.CreateCall(fn, {$in}); |
| }]; |
| } |
| |
| def LLVM_Prefetch : LLVM_ZeroResultOp<"intr.prefetch">, |
| Arguments<(ins LLVM_Type:$addr, LLVM_Type:$rw, |
| LLVM_Type:$hint, LLVM_Type:$cache)> { |
| let llvmBuilder = [{ |
| llvm::Module *module = builder.GetInsertBlock()->getModule(); |
| llvm::Function *fn = llvm::Intrinsic::getDeclaration( |
| module, llvm::Intrinsic::prefetch, $addr->getType()); |
| builder.CreateCall(fn, {$addr, $rw, $hint, $cache}); |
| }]; |
| } |
| |
| // |
| // Vector Reductions. |
| // |
| |
| def LLVM_experimental_vector_reduce_add : LLVM_VectorReduction<"add">; |
| def LLVM_experimental_vector_reduce_and : LLVM_VectorReduction<"and">; |
| def LLVM_experimental_vector_reduce_mul : LLVM_VectorReduction<"mul">; |
| def LLVM_experimental_vector_reduce_fmax : LLVM_VectorReduction<"fmax">; |
| def LLVM_experimental_vector_reduce_fmin : LLVM_VectorReduction<"fmin">; |
| def LLVM_experimental_vector_reduce_or : LLVM_VectorReduction<"or">; |
| def LLVM_experimental_vector_reduce_smax : LLVM_VectorReduction<"smax">; |
| def LLVM_experimental_vector_reduce_smin : LLVM_VectorReduction<"smin">; |
| def LLVM_experimental_vector_reduce_umax : LLVM_VectorReduction<"umax">; |
| def LLVM_experimental_vector_reduce_umin : LLVM_VectorReduction<"umin">; |
| def LLVM_experimental_vector_reduce_xor : LLVM_VectorReduction<"xor">; |
| |
| def LLVM_experimental_vector_reduce_v2_fadd : LLVM_VectorReductionV2<"fadd">; |
| def LLVM_experimental_vector_reduce_v2_fmul : LLVM_VectorReductionV2<"fmul">; |
| |
| // |
| // Atomic operations. |
| // |
| |
| def AtomicBinOpXchg : I64EnumAttrCase<"xchg", 0>; |
| def AtomicBinOpAdd : I64EnumAttrCase<"add", 1>; |
| def AtomicBinOpSub : I64EnumAttrCase<"sub", 2>; |
| def AtomicBinOpAnd : I64EnumAttrCase<"_and", 3>; |
| def AtomicBinOpNand : I64EnumAttrCase<"nand", 4>; |
| def AtomicBinOpOr : I64EnumAttrCase<"_or", 5>; |
| def AtomicBinOpXor : I64EnumAttrCase<"_xor", 6>; |
| def AtomicBinOpMax : I64EnumAttrCase<"max", 7>; |
| def AtomicBinOpMin : I64EnumAttrCase<"min", 8>; |
| def AtomicBinOpUMax : I64EnumAttrCase<"umax", 9>; |
| def AtomicBinOpUMin : I64EnumAttrCase<"umin", 10>; |
| def AtomicBinOpFAdd : I64EnumAttrCase<"fadd", 11>; |
| def AtomicBinOpFSub : I64EnumAttrCase<"fsub", 12>; |
| def AtomicBinOp : I64EnumAttr< |
| "AtomicBinOp", |
| "llvm.atomicrmw binary operations", |
| [AtomicBinOpXchg, AtomicBinOpAdd, AtomicBinOpSub, AtomicBinOpAnd, |
| AtomicBinOpNand, AtomicBinOpOr, AtomicBinOpXor, AtomicBinOpMax, |
| AtomicBinOpMin, AtomicBinOpUMax, AtomicBinOpUMin, AtomicBinOpFAdd, |
| AtomicBinOpFSub]> { |
| let cppNamespace = "::mlir::LLVM"; |
| } |
| |
| def AtomicOrderingNotAtomic : I64EnumAttrCase<"not_atomic", 0>; |
| def AtomicOrderingUnordered : I64EnumAttrCase<"unordered", 1>; |
| def AtomicOrderingMonotonic : I64EnumAttrCase<"monotonic", 2>; |
| def AtomicOrderingAcquire : I64EnumAttrCase<"acquire", 4>; |
| def AtomicOrderingRelease : I64EnumAttrCase<"release", 5>; |
| def AtomicOrderingAcquireRelease : I64EnumAttrCase<"acq_rel", 6>; |
| def AtomicOrderingSequentiallyConsistent : I64EnumAttrCase<"seq_cst", 7>; |
| def AtomicOrdering : I64EnumAttr< |
| "AtomicOrdering", |
| "Atomic ordering for LLVM's memory model", |
| [AtomicOrderingNotAtomic, AtomicOrderingUnordered, AtomicOrderingMonotonic, |
| AtomicOrderingAcquire, AtomicOrderingRelease, AtomicOrderingAcquireRelease, |
| AtomicOrderingSequentiallyConsistent]> { |
| let cppNamespace = "::mlir::LLVM"; |
| } |
| |
| def LLVM_AtomicRMWOp : LLVM_Op<"atomicrmw">, |
| Arguments<(ins AtomicBinOp:$bin_op, LLVM_Type:$ptr, LLVM_Type:$val, |
| AtomicOrdering:$ordering)>, |
| Results<(outs LLVM_Type:$res)> { |
| let llvmBuilder = [{ |
| $res = builder.CreateAtomicRMW(getLLVMAtomicBinOp($bin_op), $ptr, $val, |
| getLLVMAtomicOrdering($ordering)); |
| }]; |
| let parser = [{ return parseAtomicRMWOp(parser, result); }]; |
| let printer = [{ printAtomicRMWOp(p, *this); }]; |
| let verifier = "return ::verify(*this);"; |
| } |
| |
| def LLVM_AtomicCmpXchgOp : LLVM_Op<"cmpxchg">, |
| Arguments<(ins LLVM_Type:$ptr, LLVM_Type:$cmp, LLVM_Type:$val, |
| AtomicOrdering:$success_ordering, |
| AtomicOrdering:$failure_ordering)>, |
| Results<(outs LLVM_Type:$res)> { |
| let llvmBuilder = [{ |
| $res = builder.CreateAtomicCmpXchg($ptr, $cmp, $val, |
| getLLVMAtomicOrdering($success_ordering), |
| getLLVMAtomicOrdering($failure_ordering)); |
| }]; |
| let parser = [{ return parseAtomicCmpXchgOp(parser, result); }]; |
| let printer = [{ printAtomicCmpXchgOp(p, *this); }]; |
| let verifier = "return ::verify(*this);"; |
| } |
| |
| def LLVM_AssumeOp : LLVM_Op<"intr.assume", []>, |
| Arguments<(ins LLVM_Type:$cond)> { |
| let llvmBuilder = [{ |
| llvm::Module *module = builder.GetInsertBlock()->getModule(); |
| llvm::Function *fn = |
| llvm::Intrinsic::getDeclaration(module, llvm::Intrinsic::assume, {}); |
| builder.CreateCall(fn, {$cond}); |
| }]; |
| } |
| |
| #endif // LLVMIR_OPS |